BukuBot/slixmark.py
2024-02-28 23:22:02 +00:00

1001 lines
46 KiB
Python

#!/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 <ID> <TEXT>"
"\n"
" Modify bookmark title."
"\n"
"mod note <ID> <TEXT>"
"\n"
" Modify bookmark description."
"\n"
"tag [+|-] <ID> [tag1,tag2,tag3,...]"
"\n"
" Modify bookmark tags. Appends or deletes tags, if flag tag "
"is preceded by \'+\' or \'-\' respectively."
"\n"
"del <ID> or <URL>"
"\n"
" Delete a bookmark by ID or URL."
"\n"
"\n"
"id <ID>"
"\n"
" Print a bookmark by ID."
"\n"
"last"
"\n"
" Print most recently bookmarked item."
"\n"
"tag <TEXT>"
"\n"
" Search bookmarks of given tag."
"\n"
"search <TEXT>"
"\n"
" Search bookmarks by a given search query."
"\n"
"search any <TEXT>"
"\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) <ID> or <URL>; '
'(3) <TEXT>.')
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) <ID> or <URL>; '
'(3) <TAGS>.')
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()