#!/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 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 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') 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.') class Slixmark(slixmpp.ClientXMPP): """ Slixmark - Bookmark manager bot for Jabber/XMPP. Slixmark is a bookmark manager bot based on buku and slixmpp. """ def __init__(self, jid, password): slixmpp.ClientXMPP.__init__(self, jid, password) # 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) # 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) async def start(self, event): """ Process the session_start event. Typical actions for the session_start event are requesting the roster and broadcasting an initial presence stanza. Arguments: event -- An empty dictionary. The session_start event does not provide any additional data. """ self.command_list() self.send_presence() # await self.get_roster() await self['xep_0115'].update_caps() async def discovery(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, True) case 'last': idx = bookmarks_db.get_max_id() result = bookmarks_db.get_rec_by_id(idx) message_body = self.format_message(result, False) 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, False) + '\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, False) + '\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, 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, 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, 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, False) + '\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): 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 def command_list(self): self['xep_0050'].add_command(node='add', name='πŸ”–οΈ Add', handler=self._handle_add) self['xep_0050'].add_command(node='random', name='🎲️ Random', handler=self._handle_random) self['xep_0050'].add_command(node='modify', name='πŸ“‘οΈ Browse', handler=self._handle_browse) self['xep_0050'].add_command(node='search', name='πŸ”οΈ Search', handler=self._handle_search) # self['xep_0050'].add_command(node='tag', # name='🏷️ Tags', # handler=self._handle_tag) # self['xep_0050'].add_command(node='statistics', # name='πŸ“ŠοΈ Statistics', # handler=self._handle_stats) self['xep_0050'].add_command(node='help', name='πŸ“”οΈ Help', handler=self._handle_help) self['xep_0050'].add_command(node='license', name='βœ’οΈ License', handler=self._handle_license) self['xep_0050'].add_command(node='about', name='πŸ“œοΈ About', handler=self._handle_about) def _handle_add(self, iq, session): # jid = session['from'].bare # jid_file = jid # db_file = config.get_pathname_to_database(jid_file) form = self['xep_0004'].make_form('form', 'Add') form['instructions'] = 'Add a new bookmark' form.add_field(desc='URL to bookmark.', ftype='text-single', label='URL', required=True, var='url') form.add_field(desc='Title to add manually.', ftype='text-single', label='Title', var='title') form.add_field(desc='Description of the bookmark.', ftype='text-multi', label='Note', var='note') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', var='tag') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', var='immutable') session['has_next'] = True session['next'] = self._handle_edit_single session['payload'] = form return session def _handle_edit_single(self, payload, session): jid_bare = session['from'].bare bookmarks_db = Actions.init_db(jid_bare) form = self['xep_0004'].make_form('form', 'Edit') form['instructions'] = 'Edit bookmark' url = payload['values']['url'] idx = bookmarks_db.get_rec_id(url) if not idx: immu = payload['values']['immutable'] desc = payload['values']['note'] tags = payload['values']['tag'] name = payload['values']['title'] fetch = True if not name else False idx = bookmarks_db.add_rec(desc=desc, fetch=fetch, immutable=immu, tags_in=tags, url=url) bookmark = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='text-single', label='ID #', value=str(bookmark.id)) form.add_field(ftype='text-single', label='Title', value=bookmark.title) form.add_field(ftype='text-single', label='URL', value=bookmark.url) form.add_field(ftype='text-multi', label='Note', value=bookmark.desc) options = form.add_field(ftype='list-multi', label='Tags') for tag in bookmark.tags_raw.split(','): options.addOption(tag, tag) form.add_field(ftype='boolean', label='Immutable', value=str(bookmark.flags)) session['allow_complete'] = True session['has_next'] = False session['next'] = None session['payload'] = form return session def _handle_browse(self, iq, session): form = self['xep_0004'].make_form('form', 'Browse') form['instructions'] = 'Browse bookmark' options = form.add_field(desc='Items per page.', label='Items', ftype='list-single', value='20', var='limit') i = 10 while i <= 50: x = str(i) options.addOption(x, x) i += 10 # options['validate']['datatype'] = 'xs:integer' # options['validate']['range'] = { 'minimum': 10, 'maximum': 50 } form.add_field(ftype='hidden', value='0', var='count') session['has_next'] = True session['next'] = self._handle_browse_all session['payload'] = form return session def _handle_tag(self, iq, session): jid_bare = session['from'].bare bookmarks_db = Actions.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.', ftype='list-single', label='Tags', var='tag') tags = bookmarks_db.get_tag_all() counter = 0 for tag in tags[0]: if counter == 100: break options.addOption(tag, tag) counter += 1 session['has_next'] = True session['next'] = self._handle_browse_all session['payload'] = form return session def _handle_browse_all(self, payload, session): jid_bare = session['from'].bare bookmarks_db = Actions.init_db(jid_bare) vals = payload['values'] if 'url' in vals and vals['url']: act = vals['action'] url = vals['url'] form = self['xep_0004'].make_form('form', 'Edit') match act: case 'edit': if len(url) > 1: form['instructions'] = 'Modify bookmarks' idxs = '' tags = '' for i in url: idx = bookmarks_db.get_rec_id(i) idxs += ',' + str(idx) bookmark = bookmarks_db.get_rec_by_id(idx) tags += bookmark.tags_raw form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', value=bookmark.tags_raw, var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', value=True, var='immutable') form.add_field(ftype='hidden', value=idxs, var='ids') # session['next'] = self._handle_edit_single(payload, session) else: form['instructions'] = 'Modify bookmark' idx = bookmarks_db.get_rec_id(url[0]) bookmark = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='fixed', label='ID #', value=str(bookmark.id), var='id') form.add_field(ftype='text-single', label='Title', value=bookmark.title, var='title') form.add_field(ftype='text-single', label='URL', value=bookmark.url, var='url') form.add_field(ftype='text-multi', label='Note', value=bookmark.desc, var='description') form.add_field(ftype='hidden', value=bookmark.tags_raw, var='tags_old') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', value=bookmark.tags_raw, var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', value=str(bookmark.flags), var='immutable') case 'remove': form['instructions'] = ('The following items were deleted from ' 'bookmarks.\nProceed to finish or ' 'select items to restore.') options = form.add_field(desc='Select items to restore', ftype='list-multi', label='Deleted items', var='url') for i in url: idx = bookmarks_db.get_rec_id(i) rec = bookmarks_db.get_rec_by_id(idx) bookmarks_db.delete_rec(idx) options.addOption(rec.title, i) session['cancel'] = self._handle_cancel # session['allow_complete'] = True session['has_next'] = True session['next'] = self._handle_action_result session['payload'] = form else: limit = vals['limit'] if 'limit' in vals else 0 if isinstance(limit, list): limit = limit[0] count = vals['count'] if 'count' in vals else 0 if isinstance(count, list): count = count[0] form = self['xep_0004'].make_form('form', 'Browse') form['instructions'] = ('Select bookmarks to modify') options = form.add_field(desc='Select an action', ftype='list-single', label='Action', required=True, value='edit', var='action') options.addOption('Edit', 'edit') options.addOption('Remove', 'remove') options = form.add_field(desc='Selection of several bookmarks will ' 'only allow to modify tags.', ftype='list-multi', label='Bookmark', var='url') bookmarks = bookmarks_db.get_rec_all() # bookmarks = sorted(bookmarks, key=lambda x: x.title) counter = int(count) limiter = counter + int(limit) for bookmark in bookmarks[counter:limiter]: if counter == limiter: break options.addOption(bookmark.title, bookmark.url) counter += 1 form.add_field(ftype='hidden', value=str(counter), var='count') form.add_field(ftype='hidden', value=str(limit), var='limit') session['has_next'] = True session['next'] = self._handle_browse_all session['payload'] = form return session def _handle_action_result(self, payload, session): jid_bare = session['from'].bare bookmarks_db = Actions.init_db(jid_bare) vals = payload['values'] if 'id' in vals: idx = vals['id'] idx = int(idx) tags_new = vals['tags_new'] tags_new = tags_new.replace(' ,', ',') tags_new = tags_new.replace(', ', ',') tags_old = vals['tags_old'][0] tags = tags_old.split(',') tags_to_remove = '-,' for tag in tags: if tag not in tags_new: tags_to_remove += ',' + tag bookmarks_db.update_rec(idx, url=vals['url'], title_in=vals['title'], tags_in='+,' + tags_new, desc=vals['description'], immutable=vals['immutable']) bookmarks_db.update_rec(idx, tags_in=tags_to_remove) form = self['xep_0004'].make_form('result', 'Done') rec = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='text-single', label='Title', value=rec.title) form.add_field(ftype='text-single', label='URL', value=rec.url) form.add_field(ftype='text-single', label='Note', value=rec.desc) form.add_field(ftype='text-single', label='Tags', value=rec.tags_raw) form.add_field(ftype='text-single', label='Immutable', value=str(rec.flags)) elif 'ids' in vals: immutable = vals['immutable'] tags_new = vals['tags_new'] tags_new = tags_new.replace(' ,', ',') tags_new = tags_new.replace(', ', ',') idxs = vals['ids'].split(',') for idx in idxs: bookmarks_db.update_rec(idx, tags_in='+,' + tags_new, immutable=immutable) form = self['xep_0004'].make_form('result', 'Done') form.add_field(ftype='text-single', label='Tags', value=tags_new) form.add_field(ftype='text-single', label='Immutable', value=immutable) elif 'url' in vals: url = vals['url'] form = self['xep_0004'].make_form('result', 'Add') form['instructions'] = ('The following items have been added to ' 'the bookmarks\nNote: You will have to ' 'manually tag these items, if you would.') for i in url: idx = bookmarks_db.add_rec(url=i) rec = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='fixed', value=str(rec.id)) form.add_field(ftype='text-single', value=rec.title) form.add_field(ftype='text-single', value=rec.url) session['allow_complete'] = True session['has_next'] = False session['next'] = None session['payload'] = form return session def _handle_random(self, iq, session): jid_bare = session['from'].bare bookmarks_db = Actions.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.add_field(ftype='fixed', # ftype='text-single', label='URL', # required=True, value=bookmark.url, var='url') form.add_field(ftype='text-single', label='Title', value=bookmark.title, var='title') form.add_field(ftype='text-multi', label='Note', value=bookmark.desc, var='note') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', value=bookmark.tags_raw, var='tag') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', value=str(bookmark.flags), var='immutable') session['has_next'] = True session['next'] = self._handle_edit_single session['payload'] = form else: text_note = ('There are no bookmarks, yet.') session['notes'] = [['info', text_note]] return session def _handle_search(self, iq, session): form = self['xep_0004'].make_form('form', 'Search') form['instructions'] = 'Search for bookmarks' form.add_field(desc='Enter a search term to query.', ftype='text-single', label='Search', var='query') options = form.add_field(desc='Select type of search.', ftype='list-single', label='Search by', value='any', var='type') options.addOption('All keywords', 'all') options.addOption('Any keyword', 'any') options.addOption('Tag', 'tag') form.add_field(desc='Search for matching substrings.', ftype='boolean', label='Deep', value=True, var='deep') form.add_field(desc='Match a regular expression.', ftype='boolean', label='Regular Expression', var='regex') session['allow_prev'] = False session['has_next'] = True session['next'] = self._handle_search_result session['payload'] = form session['prev'] = None return session def _handle_search_result(self, payload, session): jid_bare = session['from'].bare bookmarks_db = Actions.init_db(jid_bare) vals = payload['values'] if 'url' in vals and vals['url']: act = vals['action'] url = vals['url'] form = self['xep_0004'].make_form('form', 'Edit') match act: case 'edit': if len(url) > 1: form['instructions'] = 'Modify bookmarks' idxs = '' tags = '' for i in url: idx = bookmarks_db.get_rec_id(i) idxs += ',' + str(idx) bookmark = bookmarks_db.get_rec_by_id(idx) tags += bookmark.tags_raw form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', value=bookmark.tags_raw, var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', value=True, var='immutable') form.add_field(ftype='hidden', value=idxs, var='ids') # session['next'] = self._handle_edit_single(payload, session) else: form['instructions'] = 'Modify bookmark' idx = bookmarks_db.get_rec_id(url[0]) bookmark = bookmarks_db.get_rec_by_id(idx) form.add_field(ftype='fixed', label='ID #', value=str(bookmark.id), var='id') form.add_field(ftype='text-single', label='Title', value=bookmark.title, var='title') form.add_field(ftype='text-single', label='URL', value=bookmark.url, var='url') form.add_field(ftype='text-multi', label='Note', value=bookmark.desc, var='description') form.add_field(ftype='hidden', value=bookmark.tags_raw, var='tags_old') form.add_field(desc='Comma-separated tags.', ftype='text-single', label='Tags', value=bookmark.tags_raw, var='tags_new') form.add_field(desc='Check to disable automatic title fetch.', ftype='boolean', label='Immutable', value=str(bookmark.flags), var='immutable') case 'remove': form['instructions'] = ('The following items were deleted from ' 'bookmarks.\nProceed to finish or ' 'select items to restore.') options = form.add_field(desc='Select items to restore', ftype='list-multi', label='Deleted items', var='url') for i in url: idx = bookmarks_db.get_rec_id(i) rec = bookmarks_db.get_rec_by_id(idx) bookmarks_db.delete_rec(idx) options.addOption(rec.title, i) session['cancel'] = self._handle_cancel # session['allow_complete'] = True session['has_next'] = True session['next'] = self._handle_action_result session['payload'] = form else: count = vals['count'] if 'count' in vals else 0 if isinstance(count, list): count = count[0] # count = count if count else 0 query = vals['query'] if isinstance(query, list): query = query[0] stype = vals['type'] if isinstance(stype, list): stype = stype[0] deep = vals['deep'] if isinstance(deep, list): deep = deep[0] deep = True if '1' else False regex = vals['regex'] if isinstance(regex, list): regex = regex[0] regex = True if '1' else False match stype: case 'all': bookmarks = bookmarks_db.searchdb(query, all_keywords=True, deep=deep, regex=regex) case 'any': bookmarks = bookmarks_db.searchdb(query, all_keywords=False, deep=deep, regex=regex) case 'tag': bookmarks = bookmarks_db.search_by_tag(query) # bookmarks = sorted(bookmarks, key=lambda x: x.title) if bookmarks: form = self['xep_0004'].make_form('form', 'Browse') form['instructions'] = ('Select bookmarks to modify') options = form.add_field(desc='Select an action', ftype='list-single', label='Action', required=True, value='edit', var='action') options.addOption('Edit', 'edit') options.addOption('Remove', 'remove') options = form.add_field(desc='Selection of several bookmarks ' 'will only allow to modify tags.', ftype='list-multi', label='Bookmark', var='url') counter = int(count) limiter = counter + 10 for bookmark in bookmarks[counter:limiter]: if counter == limiter: break options.addOption(bookmark.title, bookmark.url) counter += 1 form.add_field(ftype='hidden', value=str(counter), var='count') form.add_field(ftype='hidden', value=query, var='query') form.add_field(ftype='hidden', value=stype, var='type') deep = '1'if deep else '' form.add_field(ftype='hidden', value=deep, var='deep') regex = '1'if regex else '' form.add_field(ftype='hidden', value=regex, var='regex') session['has_next'] = True session['next'] = self._handle_search_result session['payload'] = form else: text_note = 'No results were yielded for: {}'.format(query) session['notes'] = [['info', text_note]] session['allow_prev'] = True session['prev'] = self._handle_search return session def _handle_cancel(self, payload, session): text_note = ('Operation has been cancelled.' '\n\n' 'No action was taken.') session['notes'] = [['info', text_note]] return session def _handle_help(self, iq, session): text_note = Documentation.commands() session['notes'] = [['info', text_note]] return session def _handle_license(self, iq, session): text_note = Documentation.notice() session['notes'] = [['info', text_note]] return session def _handle_about(self, iq, session): text_note = Documentation.about() session['notes'] = [['info', text_note]] 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()