From d03839d5785cbf6e39221c629d7420781b49d148 Mon Sep 17 00:00:00 2001 From: Schimon Jehudah Date: Fri, 5 Apr 2024 15:00:19 +0000 Subject: [PATCH] Rename Slixmark to buku-xmpp; Segregate code into modules. --- __main__.py | 66 +++++ about.py | 99 ++++++++ config.py | 27 ++ log.py | 47 ++++ version.py | 2 + xmpp/chat.py | 182 ++++++++++++++ slixmark.py => xmpp/client.py | 461 ++++++---------------------------- 7 files changed, 502 insertions(+), 382 deletions(-) create mode 100644 __main__.py create mode 100644 about.py create mode 100644 config.py create mode 100644 log.py create mode 100644 version.py create mode 100644 xmpp/chat.py rename slixmark.py => xmpp/client.py (60%) diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..2add40b --- /dev/null +++ b/__main__.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +# Slixmark: The XMPP Bookmark Manager +# Copyright (C) 2024 Schimon Zackary +# This file is part of Slixmark. +# See the file LICENSE for copying permission. + +import buku +from bukuxmpp.about import Documentation +from bukuxmpp.config import Configuration +from bukuxmpp.xmpp.chat import Chat +from bukuxmpp.xmpp.client import Client +from getpass import getpass +from argparse import ArgumentParser +import logging +import os +import slixmpp +import sys + +# bookmarks_db = buku.BukuDb(dbfile='temp.db') +# bookmarks_db.get_tag_all +# bookmarks_db.search_keywords_and_filter_by_tags +# bookmarks_db.exclude_results_from_search + + + + +def main(): + # Setup the command line arguments. + parser = ArgumentParser(description=Client.__doc__) + + # Output verbosity options. + parser.add_argument("-q", "--quiet", help="set logging to ERROR", + action="store_const", dest="loglevel", + const=logging.ERROR, default=logging.INFO) + parser.add_argument("-d", "--debug", help="set logging to DEBUG", + action="store_const", dest="loglevel", + const=logging.DEBUG, default=logging.INFO) + + # JID and password options. + parser.add_argument("-j", "--jid", dest="jid", + help="JID to use") + parser.add_argument("-p", "--password", dest="password", + help="password to use") + + args = parser.parse_args() + + # Setup logging. + logging.basicConfig(level=args.loglevel, + format='%(levelname)-8s %(message)s') + + if args.jid is None: + args.jid = input("Username: ") + if args.password is None: + args.password = getpass("Password: ") + + # Setup the bot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = Client(args.jid, args.password) + xmpp.connect() + xmpp.process() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/about.py b/about.py new file mode 100644 index 0000000..4f7262d --- /dev/null +++ b/about.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +class Documentation: + + def about(): + return ('Slixmark' + '\n' + 'Jabber/XMPP Bookmark Manager' + '\n\n' + 'Slixmark is an XMPP bot that facilitates accessing and ' + 'managing bookmarks remotely.' + '\n\n' + 'Slixmark is written in Python' + '\n\n' + 'It is utilizing buku to handle bookmarks, and slixmpp to ' + 'communicate in the XMPP telecommunication network.' + '\n\n' + 'https://gitgud.io/sjehuda/slixmark' + '\n\n' + 'Copyright 2024 Schimon Zackary' + '\n\n' + 'Made in Switzerland' + '\n\n' + '🇨🇭️') + + def commands(): + return ("add URL [tag1,tag2,tag3,...]" + "\n" + " Bookmark URL along with comma-separated tags." + "\n\n" + "mod name " + "\n" + " Modify bookmark title." + "\n" + "mod note " + "\n" + " Modify bookmark description." + "\n" + "tag [+|-] [tag1,tag2,tag3,...]" + "\n" + " Modify bookmark tags. Appends or deletes tags, if flag tag " + "is preceded by \'+\' or \'-\' respectively." + "\n" + "del or " + "\n" + " Delete a bookmark by ID or URL." + "\n" + "\n" + "id " + "\n" + " Print a bookmark by ID." + "\n" + "last" + "\n" + " Print most recently bookmarked item." + "\n" + "tag " + "\n" + " Search bookmarks of given tag." + "\n" + "search " + "\n" + " Search bookmarks by a given search query." + "\n" + "search any " + "\n" + " Search bookmarks by a any given keyword." + # "\n" + # "regex" + # "\n" + # " Search bookmarks using Regular Expression." + "\n") + + def notice(): + return ('Copyright 2024 Schimon Zackary Jehudah' + '\n\n' + 'Permission is hereby granted, free of charge, to any person ' + 'obtaining a copy of this software and associated ' + 'documentation files (the “Software”), to deal in the ' + 'Software without restriction, including without limitation ' + 'the rights to use, copy, modify, merge, publish, distribute, ' + 'sublicense, and/or sell copies of the Software, and to ' + 'permit persons to whom the Software is furnished to do so, ' + 'subject to the following conditions:' + '\n\n' + 'The above copyright notice and this permission notice shall ' + 'be included in all copies or substantial portions of the ' + 'Software.' + '\n\n' + 'THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY ' + 'KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ' + 'WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR ' + 'PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR ' + 'COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ' + 'LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR ' + 'OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ' + 'SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.') + diff --git a/config.py b/config.py new file mode 100644 index 0000000..108fcd4 --- /dev/null +++ b/config.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import buku +import os +import sys + +class Configuration: + + def init_db(jid_bare): + filename = jid_bare + '.db' + data_dir = Configuration.get_db_directory() + pathname = data_dir + '/' + filename + bookmarks_db = buku.BukuDb(dbfile=pathname) + return bookmarks_db + + def get_db_directory(): + if os.environ.get('HOME'): + data_home = os.path.join(os.environ.get('HOME'), '.local', 'share') + return os.path.join(data_home, 'bukuxmpp') + elif sys.platform == 'win32': + data_home = os.environ.get('APPDATA') + if data_home is None: + return os.path.join( + os.path.dirname(__file__) + '/bukuxmpp_data') + else: + return os.path.join(os.path.dirname(__file__) + '/bukuxmpp_data') diff --git a/log.py b/log.py new file mode 100644 index 0000000..12d201b --- /dev/null +++ b/log.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" + +To use this class, first, instantiate Logger with the name of your module +or class, then call the appropriate logging methods on that instance. + +logger = Logger(__name__) +logger.debug('This is a debug message') + +""" + +import logging + +class Logger: + + def __init__(self, name): + self.logger = logging.getLogger(name) + self.logger.setLevel(logging.WARNING) + + ch = logging.StreamHandler() + ch.setLevel(logging.WARNING) + + formatter = logging.Formatter('[%(asctime)s] %(levelname)s: %(name)s: %(message)s') + ch.setFormatter(formatter) + + self.logger.addHandler(ch) + + def critical(self, message): + self.logger.critical(message) + + def debug(self, message): + self.logger.debug(message) + + def error(self, message): + self.logger.error(message) + + def info(self, message): + self.logger.info(message) + + def warning(self, message): + self.logger.warning(message) + + # def check_difference(function_name, difference): + # if difference > 1: + # Logger.warning(message) diff --git a/version.py b/version.py new file mode 100644 index 0000000..d5f14d3 --- /dev/null +++ b/version.py @@ -0,0 +1,2 @@ +__version__ = '0.0.2' +__version_info__ = (0, 0, 2) diff --git a/xmpp/chat.py b/xmpp/chat.py new file mode 100644 index 0000000..86d46be --- /dev/null +++ b/xmpp/chat.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from bukuxmpp.about import Documentation +from bukuxmpp.config import Configuration + +try: + import tomllib +except: + import tomli as tomllib + + +class Chat: + + def action(self, message): + """ + Process incoming message stanzas. Be aware that this also + includes MUC messages and error messages. It is usually + a good idea to check the messages's type before processing + or sending replies. + + Arguments: + message -- The received message stanza. See the documentation + for stanza objects and the Message stanza to see + how it may be used. + """ + jid_bare = message['from'].bare + bookmarks_db = Configuration.init_db(jid_bare) + if message['type'] in ('chat', 'normal'): + message_text = " ".join(message['body'].split()) + message_lowercase = message_text.lower() + match message_lowercase: + case 'help': + message_body = '```\n' + Documentation.commands() + '\n```' + case _ if message_lowercase.startswith('add '): + message_lowercase_split = message_lowercase[4:].split(' ') + link = message_lowercase_split[0] + tags = ' '.join(message_lowercase_split[1:]) + tags = tags.replace(' ,', ',') + tags = tags.replace(', ', ',') + idx = bookmarks_db.get_rec_id(link) + if idx: + message_body = '*URL already exists.*' + else: + idx = bookmarks_db.add_rec(url=link, tags_in=tags) + message_body = ('*New bookmark has been added as {}.*' + .format(idx)) + case _ if message_lowercase.startswith('id'): + idx = message_lowercase[2:] + result = bookmarks_db.get_rec_by_id(idx) + message_body = Chat.format_message(result, extended=True) + case 'last': + idx = bookmarks_db.get_max_id() + result = bookmarks_db.get_rec_by_id(idx) + message_body = Chat.format_message(result) + case _ if message_lowercase.startswith('search any '): + query = message_lowercase[11:] + query = query.split(' ') + results = bookmarks_db.searchdb(query, + all_keywords=False, + deep=True, + regex=False) + message_body = '*Results for query: {}*\n\n'.format(query) + for result in results: + message_body += Chat.format_message(result) + '\n\n' + message_body += '*Total of {} results*'.format(len(results)) + case _ if message_lowercase.startswith('search '): + query = message_lowercase[7:] + query = query.split(' ') + results = bookmarks_db.searchdb(query, + all_keywords=True, + deep=True, + regex=False) + message_body = '*Results for query: {}*\n\n'.format(query) + for result in results: + message_body += Chat.format_message(result) + '\n\n' + message_body += '*Total of {} results*\n\n'.format(len(results)) + # elif message.startswith('regex'): + # message_body = bookmark_regexp(message[7:]) + case _ if message_lowercase.startswith('del '): + val = message_lowercase[4:] + try: + idx = int(val) + except: + idx = bookmarks_db.get_rec_id(val) + bookmark = bookmarks_db.get_rec_by_id(idx) + message_body = Chat.format_message(bookmark, extended=True) if bookmark else '' + result = bookmarks_db.delete_rec(idx) + if result: + message_body += '\n*Bookmark has been deleted.*' + else: + message_body += '\n*No action has been taken for index {}*'.format(idx) + case _ if message_lowercase.startswith('mod '): + message_lowercase_split = message_lowercase[4:].split(' ') + if len(message_lowercase_split) > 2: + arg = message_lowercase_split[0] + val = message_lowercase_split[1] + new = ' '.join(message_lowercase_split[2:]) + message_body = '' + try: + idx = int(val) + except: + idx = bookmarks_db.get_rec_id(val) + match arg: + case 'name': + result = bookmarks_db.update_rec(idx, title_in=new) + case 'note': + result = bookmarks_db.update_rec(idx, desc=new) + case _: + result = None + message_body = ('*Invalid argument. ' + 'Must be "name" or "note".*\n') + bookmark = bookmarks_db.get_rec_by_id(idx) + message_body += Chat.format_message(bookmark, extended=True) if bookmark else '' + if result: + message_body += '\n*Bookmark has been deleted.*' + else: + message_body += '\n*No action has been taken for index {}*'.format(idx) + else: + message_body = ('Missing argument. ' + 'Require three arguments: ' + '(1) "name" or "note"; ' + '(2) or ; ' + '(3) .') + case _ if (message_lowercase.startswith('tag +') or + message_lowercase.startswith('tag -')): + message_lowercase_split = message_lowercase[4:].split(' ') + if len(message_lowercase_split) > 2: + arg = message_lowercase_split[0] + val = message_lowercase_split[1] + try: + idx = int(val) + except: + idx = bookmarks_db.get_rec_id(val) + # tag = ',' + ' '.join(message_lowercase_split[2:]) + ',' + # tag = ' '.join(message_lowercase_split[2:]) + tag = arg + ',' + ' '.join(message_lowercase_split[2:]) + tag = tag.replace(' ,', ',') + tag = tag.replace(', ', ',') + result = bookmarks_db.update_rec(idx, tags_in=tag) + bookmark = bookmarks_db.get_rec_by_id(idx) + if result: + message_body = Chat.format_message(bookmark, extended=True) if bookmark else '' + message_body += '\n*Bookmark has been updated.*' + else: + message_body = '\n*No action has been taken for index {}*'.format(idx) + else: + message_body = ('Missing argument. ' + 'Require three arguments: ' + '(1) + or - sign; ' + '(2) or ; ' + '(3) .') + case _ if message_lowercase.startswith('tag'): + tag = message_lowercase[4:] + results = bookmarks_db.search_by_tag(tag) + message_body = '*Results for tag: {}*\n\n'.format(tag) + for result in results: + message_body += Chat.format_message(result) + '\n\n' + message_body += '*Total of {} results*'.format(len(results)) + case _: + message_body = ('Unknown command. Send "help" for list ' + 'of commands.') + message.reply(message_body).send() + #message.reply("Thanks for sending\n%(body)s" % message).send() + + def format_message(bookmark, extended=False): + # idx = bookmark.id + # url = bookmark.url + # name = bookmark.title if bookmark.title else 'Untitled' + # desc = bookmark.desc if bookmark.desc else 'No comment' + idx = bookmark[0] + url = bookmark[1] + name = bookmark[2] if bookmark[2] else 'Untitled' + desc = bookmark[4] if bookmark[4] else None + # rec = '\n 🔖️ {} [{}]\n 🔗️ {}\n 🏷️ {}'.format(title, index, link, tags) + if extended: + tags = '' if bookmark.tags_raw == ',' else ", ".join(bookmark.tags_raw.split(","))[2:-2] + tags = tags if tags else 'No tags' + message_body = ('{}. {}\n{}\n{}\n{}'.format(idx, name, url, desc, tags)) + else: + message_body = ('{}. {}\n{}'.format(idx, name, url)) + return message_body diff --git a/slixmark.py b/xmpp/client.py similarity index 60% rename from slixmark.py rename to xmpp/client.py index 3dedad6..c274a71 100644 --- a/slixmark.py +++ b/xmpp/client.py @@ -1,166 +1,69 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- -# Slixmark: The XMPP Bookmark Manager -# Copyright (C) 2024 Schimon Zackary -# This file is part of Slixmark. -# See the file LICENSE for copying permission. - -import buku -from getpass import getpass -from argparse import ArgumentParser -import logging -import os import slixmpp -import sys +from bukuxmpp.xmpp.chat import Chat +from bukuxmpp.config import Configuration +from bukuxmpp.about import Documentation -# bookmarks_db = buku.BukuDb(dbfile='temp.db') -# bookmarks_db.get_tag_all -# bookmarks_db.search_keywords_and_filter_by_tags -# bookmarks_db.exclude_results_from_search +from slixfeed.log import Logger +from slixfeed.version import __version__ -class Actions: - - def init_db(jid_bare): - filename = jid_bare + '.db' - data_dir = Actions.get_db_directory() - pathname = data_dir + '/' + filename - bookmarks_db = buku.BukuDb(dbfile=pathname) - return bookmarks_db - - def get_db_directory(): - if os.environ.get('HOME'): - data_home = os.path.join(os.environ.get('HOME'), '.local', 'share') - return os.path.join(data_home, 'slixmark') - elif sys.platform == 'win32': - data_home = os.environ.get('APPDATA') - if data_home is None: - return os.path.join( - os.path.dirname(__file__) + '/slixmark_data') - else: - return os.path.join(os.path.dirname(__file__) + '/slixmark_data') +try: + import tomllib +except: + import tomli as tomllib -class Documentation: +# time_now = datetime.now() +# time_now = time_now.strftime("%H:%M:%S") - def about(): - return ('Slixmark' - '\n' - 'Jabber/XMPP Bookmark Manager' - '\n\n' - 'Slixmark is an XMPP bot that facilitates accessing and ' - 'managing bookmarks remotely.' - '\n\n' - 'Slixmark is written in Python' - '\n\n' - 'It is utilizing buku to handle bookmarks, and slixmpp to ' - 'communicate in the XMPP telecommunication network.' - '\n\n' - 'https://gitgud.io/sjehuda/slixmark' - '\n\n' - 'Copyright 2024 Schimon Zackary' - '\n\n' - 'Made in Switzerland' - '\n\n' - '🇨🇭️') +# def print_time(): +# # return datetime.now().strftime("%H:%M:%S") +# now = datetime.now() +# current_time = now.strftime("%H:%M:%S") +# return current_time - def commands(): - return ("add URL [tag1,tag2,tag3,...]" - "\n" - " Bookmark URL along with comma-separated tags." - "\n\n" - "mod name " - "\n" - " Modify bookmark title." - "\n" - "mod note " - "\n" - " Modify bookmark description." - "\n" - "tag [+|-] [tag1,tag2,tag3,...]" - "\n" - " Modify bookmark tags. Appends or deletes tags, if flag tag " - "is preceded by \'+\' or \'-\' respectively." - "\n" - "del or " - "\n" - " Delete a bookmark by ID or URL." - "\n" - "\n" - "id " - "\n" - " Print a bookmark by ID." - "\n" - "last" - "\n" - " Print most recently bookmarked item." - "\n" - "tag " - "\n" - " Search bookmarks of given tag." - "\n" - "search " - "\n" - " Search bookmarks by a given search query." - "\n" - "search any " - "\n" - " Search bookmarks by a any given keyword." - # "\n" - # "regex" - # "\n" - # " Search bookmarks using Regular Expression." - "\n") +logger = Logger(__name__) - def notice(): - return ('Copyright 2024 Schimon Zackary Jehudah' - '\n\n' - 'Permission is hereby granted, free of charge, to any person ' - 'obtaining a copy of this software and associated ' - 'documentation files (the “Software”), to deal in the ' - 'Software without restriction, including without limitation ' - 'the rights to use, copy, modify, merge, publish, distribute, ' - 'sublicense, and/or sell copies of the Software, and to ' - 'permit persons to whom the Software is furnished to do so, ' - 'subject to the following conditions:' - '\n\n' - 'The above copyright notice and this permission notice shall ' - 'be included in all copies or substantial portions of the ' - 'Software.' - '\n\n' - 'THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY ' - 'KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ' - 'WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR ' - 'PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR ' - 'COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ' - 'LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR ' - 'OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ' - 'SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.') - - -class Slixmark(slixmpp.ClientXMPP): +class Client(slixmpp.ClientXMPP): """ - Slixmark - Bookmark manager bot for Jabber/XMPP. - Slixmark is a bookmark manager bot based on buku and slixmpp. + bukuxmpp - Bookmark manager bot for Jabber/XMPP. + bukuxmpp is a bookmark manager bot based on buku and slixmpp. """ def __init__(self, jid, password): slixmpp.ClientXMPP.__init__(self, jid, password) + print('client') + + self.register_plugin('xep_0030') # Service Discovery + self.register_plugin('xep_0004') # Data Forms + self.register_plugin('xep_0060') # Publish-Subscribe + self.register_plugin('xep_0050') # Ad-Hoc Commands + self.register_plugin('xep_0115') # Entity Capabilities + self.register_plugin('xep_0122') # Data Forms Validation + self.register_plugin('xep_0199') # XMPP Ping + + # Connect to the XMPP server and start processing XMPP stanzas. + # self.connect() + # self.process() + # The session_start event will be triggered when # the bot establishes its connection with the server # and the XML streams are ready for use. We want to # listen for this event so that we we can initialize # our roster. - self.add_event_handler("session_start", self.start) + self.add_event_handler("session_start", self.process_session_start) # The message event is triggered whenever a message # stanza is received. Be aware that that includes # MUC messages and error messages. self.add_event_handler("message", self.process_message) - self.add_event_handler("disco_info", self.discovery) + self.add_event_handler("disco_info", self.process_disco_info) - async def start(self, event): + async def process_session_start(self, event): """ Process the session_start event. @@ -178,176 +81,14 @@ class Slixmark(slixmpp.ClientXMPP): # await self.get_roster() await self['xep_0115'].update_caps() - async def discovery(self, DiscoInfo): + async def process_disco_info(self, DiscoInfo): jid = DiscoInfo['from'] await self['xep_0115'].update_caps(jid=jid) # jid_bare = DiscoInfo['from'].bare # self.bookmarks_db = buku.BukuDb(dbfile=jid_bare + '.db') 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 idea to check the messages's type before processing - or sending replies. - - Arguments: - message -- The received message stanza. See the documentation - for stanza objects and the Message stanza to see - how it may be used. - """ - jid_bare = message['from'].bare - bookmarks_db = Actions.init_db(jid_bare) - if message['type'] in ('chat', 'normal'): - message_text = " ".join(message['body'].split()) - message_lowercase = message_text.lower() - match message_lowercase: - case 'help': - message_body = '```\n' + Documentation.commands() + '\n```' - case _ if message_lowercase.startswith('add '): - message_lowercase_split = message_lowercase[4:].split(' ') - link = message_lowercase_split[0] - tags = ' '.join(message_lowercase_split[1:]) - tags = tags.replace(' ,', ',') - tags = tags.replace(', ', ',') - idx = bookmarks_db.get_rec_id(link) - if idx: - message_body = '*URL already exists.*' - else: - idx = bookmarks_db.add_rec(url=link, tags_in=tags) - message_body = ('*New bookmark has been added as {}.*' - .format(idx)) - case _ if message_lowercase.startswith('id'): - idx = message_lowercase[2:] - result = bookmarks_db.get_rec_by_id(idx) - message_body = self.format_message(result, extended=True) - case 'last': - idx = bookmarks_db.get_max_id() - result = bookmarks_db.get_rec_by_id(idx) - message_body = self.format_message(result) - case _ if message_lowercase.startswith('search any '): - query = message_lowercase[11:] - query = query.split(' ') - results = bookmarks_db.searchdb(query, - all_keywords=False, - deep=True, - regex=False) - message_body = '*Results for query: {}*\n\n'.format(query) - for result in results: - message_body += self.format_message(result) + '\n\n' - message_body += '*Total of {} results*'.format(len(results)) - case _ if message_lowercase.startswith('search '): - query = message_lowercase[7:] - query = query.split(' ') - results = bookmarks_db.searchdb(query, - all_keywords=True, - deep=True, - regex=False) - message_body = '*Results for query: {}*\n\n'.format(query) - for result in results: - message_body += self.format_message(result) + '\n\n' - message_body += '*Total of {} results*\n\n'.format(len(results)) - # elif message.startswith('regex'): - # message_body = bookmark_regexp(message[7:]) - case _ if message_lowercase.startswith('del '): - val = message_lowercase[4:] - try: - idx = int(val) - except: - idx = bookmarks_db.get_rec_id(val) - bookmark = bookmarks_db.get_rec_by_id(idx) - message_body = self.format_message(bookmark, extended=True) if bookmark else '' - result = bookmarks_db.delete_rec(idx) - if result: - message_body += '\n*Bookmark has been deleted.*' - else: - message_body += '\n*No action has been taken for index {}*'.format(idx) - case _ if message_lowercase.startswith('mod '): - message_lowercase_split = message_lowercase[4:].split(' ') - if len(message_lowercase_split) > 2: - arg = message_lowercase_split[0] - val = message_lowercase_split[1] - new = ' '.join(message_lowercase_split[2:]) - message_body = '' - try: - idx = int(val) - except: - idx = bookmarks_db.get_rec_id(val) - match arg: - case 'name': - result = bookmarks_db.update_rec(idx, title_in=new) - case 'note': - result = bookmarks_db.update_rec(idx, desc=new) - case _: - result = None - message_body = ('*Invalid argument. ' - 'Must be "name" or "note".*\n') - bookmark = bookmarks_db.get_rec_by_id(idx) - message_body += self.format_message(bookmark, extended=True) if bookmark else '' - if result: - message_body += '\n*Bookmark has been deleted.*' - else: - message_body += '\n*No action has been taken for index {}*'.format(idx) - else: - message_body = ('Missing argument. ' - 'Require three arguments: ' - '(1) "name" or "note"; ' - '(2) or ; ' - '(3) .') - case _ if (message_lowercase.startswith('tag +') or - message_lowercase.startswith('tag -')): - message_lowercase_split = message_lowercase[4:].split(' ') - if len(message_lowercase_split) > 2: - arg = message_lowercase_split[0] - val = message_lowercase_split[1] - try: - idx = int(val) - except: - idx = bookmarks_db.get_rec_id(val) - # tag = ',' + ' '.join(message_lowercase_split[2:]) + ',' - # tag = ' '.join(message_lowercase_split[2:]) - tag = arg + ',' + ' '.join(message_lowercase_split[2:]) - tag = tag.replace(' ,', ',') - tag = tag.replace(', ', ',') - result = bookmarks_db.update_rec(idx, tags_in=tag) - bookmark = bookmarks_db.get_rec_by_id(idx) - if result: - message_body = self.format_message(bookmark, extended=True) if bookmark else '' - message_body += '\n*Bookmark has been updated.*' - else: - message_body = '\n*No action has been taken for index {}*'.format(idx) - else: - message_body = ('Missing argument. ' - 'Require three arguments: ' - '(1) + or - sign; ' - '(2) or ; ' - '(3) .') - case _ if message_lowercase.startswith('tag'): - tag = message_lowercase[4:] - results = bookmarks_db.search_by_tag(tag) - message_body = '*Results for tag: {}*\n\n'.format(tag) - for result in results: - message_body += self.format_message(result) + '\n\n' - message_body += '*Total of {} results*'.format(len(results)) - case _: - message_body = ('Unknown command. Send "help" for list ' - 'of commands.') - message.reply(message_body).send() - #message.reply("Thanks for sending\n%(body)s" % message).send() - - def format_message(self, bookmark, extended=False): - idx = bookmark.id - url = bookmark.url - name = bookmark.title if bookmark.title else 'Untitled' - desc = bookmark.desc if bookmark.desc else 'No comment' - # rec = '\n 🔖️ {} [{}]\n 🔗️ {}\n 🏷️ {}'.format(title, index, link, tags) - if extended: - tags = '' if bookmark.tags_raw == ',' else ", ".join(bookmark.tags_raw.split(","))[2:-2] - tags = tags if tags else 'No tags' - message_body = ('{}. {}\n{}\n{}\n{}'.format(idx, name, url, desc, tags)) - else: - message_body = ('{}. {}\n{}'.format(idx, name, url)) - return message_body + Chat.action(self, message) def command_list(self): self['xep_0050'].add_command(node='add', @@ -412,7 +153,7 @@ class Slixmark(slixmpp.ClientXMPP): def _handle_edit_single(self, payload, session): jid_bare = session['from'].bare - bookmarks_db = Actions.init_db(jid_bare) + bookmarks_db = Configuration.init_db(jid_bare) form = self['xep_0004'].make_form('form', 'Edit') form['instructions'] = 'Edit bookmark' url = payload['values']['url'] @@ -431,23 +172,23 @@ class Slixmark(slixmpp.ClientXMPP): bookmark = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='text-single', label='ID #', - value=str(bookmark.id)) + value=str(idx)) form.add_field(ftype='text-single', label='Title', - value=bookmark.title) + value=bookmark[2]) form.add_field(ftype='text-single', label='URL', - value=bookmark.url) + value=bookmark[1]) form.add_field(ftype='text-multi', label='Note', - value=bookmark.desc) + value=bookmark[4]) options = form.add_field(ftype='list-multi', label='Tags') - for tag in bookmark.tags_raw.split(','): + for tag in bookmark[3].split(','): options.addOption(tag, tag) form.add_field(ftype='boolean', label='Immutable', - value=str(bookmark.flags)) + value=str(bookmark[5])) session['allow_complete'] = True session['has_next'] = False session['next'] = None @@ -479,7 +220,7 @@ class Slixmark(slixmpp.ClientXMPP): def _handle_tag(self, iq, session): jid_bare = session['from'].bare - bookmarks_db = Actions.init_db(jid_bare) + bookmarks_db = Configuration.init_db(jid_bare) form = self['xep_0004'].make_form('form', 'Tags') form['instructions'] = ('Select tags to browse') options = form.add_field(desc='Select tag to list its items.', @@ -499,7 +240,7 @@ class Slixmark(slixmpp.ClientXMPP): def _handle_browse_all(self, payload, session): jid_bare = session['from'].bare - bookmarks_db = Actions.init_db(jid_bare) + bookmarks_db = Configuration.init_db(jid_bare) vals = payload['values'] if 'url' in vals and vals['url']: act = vals['action'] @@ -515,11 +256,11 @@ class Slixmark(slixmpp.ClientXMPP): idx = bookmarks_db.get_rec_id(i) idxs += ',' + str(idx) bookmark = bookmarks_db.get_rec_by_id(idx) - tags += bookmark.tags_raw + tags += bookmark[3] form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', - value=bookmark.tags_raw, + value=bookmark[3], var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', @@ -536,32 +277,32 @@ class Slixmark(slixmpp.ClientXMPP): bookmark = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='fixed', label='ID #', - value=str(bookmark.id), + value=str(idx), var='id') form.add_field(ftype='text-single', label='Title', - value=bookmark.title, + value=bookmark[2], var='title') form.add_field(ftype='text-single', label='URL', - value=bookmark.url, + value=bookmark[1], var='url') form.add_field(ftype='text-multi', label='Note', - value=bookmark.desc, + value=bookmark[4], var='description') form.add_field(ftype='hidden', - value=bookmark.tags_raw, + value=bookmark[3], var='tags_old') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', - value=bookmark.tags_raw, + value=bookmark[3], var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', - value=str(bookmark.flags), + value=str(bookmark[5]), var='immutable') case 'remove': form['instructions'] = ('The following items were deleted from ' @@ -607,7 +348,7 @@ class Slixmark(slixmpp.ClientXMPP): limiter = counter + int(limit) for bookmark in bookmarks[counter:limiter]: if counter == limiter: break - options.addOption(bookmark.title, bookmark.url) + options.addOption(bookmark[2], bookmark[1]) counter += 1 form.add_field(ftype='hidden', value=str(counter), @@ -622,7 +363,7 @@ class Slixmark(slixmpp.ClientXMPP): def _handle_action_result(self, payload, session): jid_bare = session['from'].bare - bookmarks_db = Actions.init_db(jid_bare) + bookmarks_db = Configuration.init_db(jid_bare) vals = payload['values'] if 'id' in vals: idx = vals['id'] @@ -688,7 +429,7 @@ class Slixmark(slixmpp.ClientXMPP): idx = bookmarks_db.add_rec(url=i) rec = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='fixed', - value=str(rec.id)) + value=str(idx)) form.add_field(ftype='text-single', value=rec.title) form.add_field(ftype='text-single', @@ -701,36 +442,36 @@ class Slixmark(slixmpp.ClientXMPP): def _handle_random(self, iq, session): jid_bare = session['from'].bare - bookmarks_db = Actions.init_db(jid_bare) + bookmarks_db = Configuration.init_db(jid_bare) bookmarks = bookmarks_db.get_rec_all() if bookmarks: import random bookmark = random.choice(bookmarks) form = self['xep_0004'].make_form('form', 'Random') - form['instructions'] = 'Bookmark #{}'.format(bookmark.id) + form['instructions'] = 'Bookmark #{}'.format(bookmark[0]) form.add_field(ftype='fixed', # ftype='text-single', label='URL', # required=True, - value=bookmark.url, + value=bookmark[1], var='url') form.add_field(ftype='text-single', label='Title', - value=bookmark.title, + value=bookmark[2], var='title') form.add_field(ftype='text-multi', label='Note', - value=bookmark.desc, + value=bookmark[4], var='note') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', - value=bookmark.tags_raw, + value=bookmark[3], var='tag') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', - value=str(bookmark.flags), + value=str(bookmark[5]), var='immutable') session['has_next'] = True session['next'] = self._handle_edit_single @@ -773,7 +514,7 @@ class Slixmark(slixmpp.ClientXMPP): def _handle_search_result(self, payload, session): jid_bare = session['from'].bare - bookmarks_db = Actions.init_db(jid_bare) + bookmarks_db = Configuration.init_db(jid_bare) vals = payload['values'] if 'url' in vals and vals['url']: act = vals['action'] @@ -789,11 +530,11 @@ class Slixmark(slixmpp.ClientXMPP): idx = bookmarks_db.get_rec_id(i) idxs += ',' + str(idx) bookmark = bookmarks_db.get_rec_by_id(idx) - tags += bookmark.tags_raw + tags += bookmark[3] form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', - value=bookmark.tags_raw, + value=bookmark[3], var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', @@ -810,32 +551,32 @@ class Slixmark(slixmpp.ClientXMPP): bookmark = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='fixed', label='ID #', - value=str(bookmark.id), + value=str(idx), var='id') form.add_field(ftype='text-single', label='Title', - value=bookmark.title, + value=bookmark[2], var='title') form.add_field(ftype='text-single', label='URL', - value=bookmark.url, + value=bookmark[1], var='url') form.add_field(ftype='text-multi', label='Note', - value=bookmark.desc, + value=bookmark[4], var='description') form.add_field(ftype='hidden', - value=bookmark.tags_raw, + value=bookmark[3], var='tags_old') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', - value=bookmark.tags_raw, + value=bookmark[3], var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', - value=str(bookmark.flags), + value=str(bookmark[5]), var='immutable') case 'remove': form['instructions'] = ('The following items were deleted from ' @@ -903,7 +644,7 @@ class Slixmark(slixmpp.ClientXMPP): limiter = counter + 10 for bookmark in bookmarks[counter:limiter]: if counter == limiter: break - options.addOption(bookmark.title, bookmark.url) + options.addOption(bookmark[2], bookmark[1]) counter += 1 form.add_field(ftype='hidden', value=str(counter), @@ -955,47 +696,3 @@ class Slixmark(slixmpp.ClientXMPP): return session -if __name__ == '__main__': - # Setup the command line arguments. - parser = ArgumentParser(description=Slixmark.__doc__) - - # Output verbosity options. - parser.add_argument("-q", "--quiet", help="set logging to ERROR", - action="store_const", dest="loglevel", - const=logging.ERROR, default=logging.INFO) - parser.add_argument("-d", "--debug", help="set logging to DEBUG", - action="store_const", dest="loglevel", - const=logging.DEBUG, default=logging.INFO) - - # JID and password options. - parser.add_argument("-j", "--jid", dest="jid", - help="JID to use") - parser.add_argument("-p", "--password", dest="password", - help="password to use") - - args = parser.parse_args() - - # Setup logging. - logging.basicConfig(level=args.loglevel, - format='%(levelname)-8s %(message)s') - - if args.jid is None: - args.jid = input("Username: ") - if args.password is None: - args.password = getpass("Password: ") - - # Setup the bot and register plugins. Note that while plugins may - # have interdependencies, the order in which you register them does - # not matter. - xmpp = Slixmark(args.jid, args.password) - xmpp.register_plugin('xep_0030') # Service Discovery - xmpp.register_plugin('xep_0004') # Data Forms - xmpp.register_plugin('xep_0060') # Publish-Subscribe - xmpp.register_plugin('xep_0050') # Ad-Hoc Commands - xmpp.register_plugin('xep_0115') # Entity Capabilities - xmpp.register_plugin('xep_0122') # Data Forms Validation - xmpp.register_plugin('xep_0199') # XMPP Ping - - # Connect to the XMPP server and start processing XMPP stanzas. - xmpp.connect() - xmpp.process()