#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ TODO 1) Deprecate "add" (see above) and make it interactive. Slixfeed: Do you still want to add this URL to subscription list? See: case _ if message_lowercase.startswith("add"): 2) If subscription is inadequate (see XmppPresence.request), send a message that says so. elif not self.client_roster[jid]["to"]: breakpoint() message.reply("Share online status to activate bot.").send() return 3) Set timeout for moderator interaction. If moderator interaction has been made, and moderator approves the bot, then the bot will add the given groupchat to bookmarks; otherwise, the bot will send a message that it was not approved and therefore leaves the groupchat. """ import asyncio from feedparser import parse import logging import os import slixfeed.action as action import slixfeed.config as config import slixfeed.crawl as crawl from slixfeed.config import Config import slixfeed.dt as dt import slixfeed.fetch as fetch import slixfeed.sqlite as sqlite import slixfeed.task as task import slixfeed.url as uri from slixfeed.version import __version__ from slixfeed.xmpp.bookmark import XmppBookmark from slixfeed.xmpp.muc import XmppGroupchat from slixfeed.xmpp.message import XmppMessage from slixfeed.xmpp.presence import XmppPresence from slixfeed.xmpp.upload import XmppUpload from slixfeed.xmpp.privilege import is_moderator, is_operator, is_access from slixfeed.xmpp.utility import get_chat_type import time from random import randrange try: import tomllib except: import tomli as tomllib # for task in main_task: # task.cancel() # Deprecated in favour of event "presence_available" # if not main_task: # await select_file() class Chat: async def process_message(self, message): """ Process incoming message stanzas. Be aware that this also includes MUC messages and error messages. It is usually a good practice to check the messages's type before processing or sending replies. Parameters ---------- message : str The received message stanza. See the documentation for stanza objects and the Message stanza to see how it may be used. """ if message['type'] in ('chat', 'groupchat', 'normal'): jid_bare = message['from'].bare jid_file = jid_bare message_text = ' '.join(message['body'].split()) command_time_start = time.time() # if (message['type'] == 'groupchat' and # message['muc']['nick'] == self.alias): # return # FIXME Code repetition. See below. # TODO Check alias by nickname associated with conference if message['type'] == 'groupchat': if (message['muc']['nick'] == self.alias): return jid_full = str(message['from']) if not is_moderator(self, jid_bare, jid_full): return # NOTE This is an exceptional case in which we treat # type groupchat the same as type chat in a way that # doesn't require an exclamation mark for actionable # command. if (message_text.lower().startswith('http') and message_text.lower().endswith('.opml')): url = message_text key_list = ['status'] task.clean_tasks_xmpp_chat(self, jid_bare, key_list) status_type = 'dnd' status_message = '📥️ Procesing request to import feeds...' # pending_tasks_num = len(self.pending_tasks[jid_bare]) pending_tasks_num = randrange(10000, 99999) self.pending_tasks[jid_bare][pending_tasks_num] = status_message # self.pending_tasks_counter += 1 # self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message XmppPresence.send(self, jid_bare, status_message, status_type=status_type) db_file = config.get_pathname_to_database(jid_file) result = await fetch.http(url) count = await action.import_opml(db_file, result) if count: response = 'Successfully imported {} feeds.'.format(count) else: response = 'OPML file was not imported.' del self.pending_tasks[jid_bare][pending_tasks_num] # del self.pending_tasks[jid_bare][self.pending_tasks_counter] key_list = ['status'] await task.start_tasks_xmpp_chat(self, jid_bare, key_list) XmppMessage.send_reply(self, message, response) return if message['type'] == 'groupchat': # nick = message['from'][message['from'].index('/')+1:] # nick = str(message['from']) # nick = nick[nick.index('/')+1:] if (message['muc']['nick'] == self.alias or not message['body'].startswith('!')): return # token = await initdb( # jid_bare, # sqlite.get_setting_value, # 'token' # ) # if token == 'accepted': # operator = await initdb( # jid_bare, # sqlite.get_setting_value, # 'masters' # ) # if operator: # if nick not in operator: # return # approved = False jid_full = str(message['from']) if not is_moderator(self, jid_bare, jid_full): return # if role == 'moderator': # approved = True # TODO Implement a list of temporary operators # Once an operator is appointed, the control would last # untile the participant has been disconnected from MUC # An operator is a function to appoint non moderators. # Changing nickname is fine and consist of no problem. # if not approved: # operator = await initdb( # jid_bare, # sqlite.get_setting_value, # 'masters' # ) # if operator: # if nick in operator: # approved = True # if not approved: # return # # Begin processing new JID # # Deprecated in favour of event 'presence_available' # db_dir = config.get_default_data_directory() # os.chdir(db_dir) # if jid + '.db' not in os.listdir(): # await task_jid(jid) # await compose.message(self, jid_bare, message) if message['type'] == 'groupchat': message_text = message_text[1:] message_lowercase = message_text.lower() logging.debug([str(message['from']), ':', message_text]) # Support private message via groupchat # See https://codeberg.org/poezio/slixmpp/issues/3506 if message['type'] == 'chat' and message.get_plugin('muc', check=True): # jid_bare = message['from'].bare jid_full = str(message['from']) if (jid_bare == jid_full[:jid_full.index('/')]): jid = str(message['from']) jid_file = jid_full.replace('/', '_') response = None match message_lowercase: # case 'breakpoint': # if is_operator(self, jid_bare): # breakpoint() # print('task_manager[jid]') # print(task_manager[jid]) # await self.get_roster() # print('roster 1') # print(self.client_roster) # print('roster 2') # print(self.client_roster.keys()) # print('jid') # print(jid) # else: # response = ( # 'This action is restricted. ' # 'Type: breakpoint.' # ) # XmppMessage.send_reply(self, message, response) case 'help': command_list = ' '.join(action.manual('commands.toml')) response = ('Available command keys:\n' '```\n{}\n```\n' 'Usage: `help `' .format(command_list)) print(response) XmppMessage.send_reply(self, message, response) case 'help all': command_list = action.manual('commands.toml', section='all') response = ('Complete list of commands:\n' '```\n{}\n```' .format(command_list)) print(response) XmppMessage.send_reply(self, message, response) case _ if message_lowercase.startswith('help'): command = message_text[5:].lower() command = command.split(' ') if len(command) == 2: command_root = command[0] command_name = command[1] command_list = action.manual('commands.toml', section=command_root, command=command_name) if command_list: command_list = ''.join(command_list) response = (command_list) else: response = ('KeyError for {} {}' .format(command_root, command_name)) elif len(command) == 1: command = command[0] command_list = action.manual('commands.toml', command) if command_list: command_list = ' '.join(command_list) response = ('Available command `{}` keys:\n' '```\n{}\n```\n' 'Usage: `help {} `' .format(command, command_list, command)) else: response = 'KeyError for {}'.format(command) else: response = ('Invalid. Enter command key ' 'or command key & name') XmppMessage.send_reply(self, message, response) case 'info': config_dir = config.get_default_config_directory() with open(config_dir + '/' + 'information.toml', mode="rb") as information: entries = tomllib.load(information) response = ('Available command options:\n' '```\n{}\n```\n' 'Usage: `info