Rename Slixmark to buku-xmpp;

Segregate code into modules.
This commit is contained in:
Schimon Jehudah 2024-04-05 15:00:19 +00:00
parent 27f843d3b6
commit d03839d578
7 changed files with 502 additions and 382 deletions

66
__main__.py Normal file
View file

@ -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()

99
about.py Normal file
View file

@ -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 <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.')

27
config.py Normal file
View file

@ -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')

47
log.py Normal file
View file

@ -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)

2
version.py Normal file
View file

@ -0,0 +1,2 @@
__version__ = '0.0.2'
__version_info__ = (0, 0, 2)

182
xmpp/chat.py Normal file
View file

@ -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) <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 = 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) <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 += 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

View file

@ -1,166 +1,69 @@
#!/usr/bin/env python3 #!/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 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') from slixfeed.log import Logger
# bookmarks_db.get_tag_all from slixfeed.version import __version__
# bookmarks_db.search_keywords_and_filter_by_tags
# bookmarks_db.exclude_results_from_search
class Actions: try:
import tomllib
def init_db(jid_bare): except:
filename = jid_bare + '.db' import tomli as tomllib
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: # time_now = datetime.now()
# time_now = time_now.strftime("%H:%M:%S")
def about(): # def print_time():
return ('Slixmark' # # return datetime.now().strftime("%H:%M:%S")
'\n' # now = datetime.now()
'Jabber/XMPP Bookmark Manager' # current_time = now.strftime("%H:%M:%S")
'\n\n' # return current_time
'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(): logger = Logger(__name__)
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(): class Client(slixmpp.ClientXMPP):
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. bukuxmpp - Bookmark manager bot for Jabber/XMPP.
Slixmark is a bookmark manager bot based on buku and slixmpp. bukuxmpp is a bookmark manager bot based on buku and slixmpp.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__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 session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # 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 # The message event is triggered whenever a message
# stanza is received. Be aware that that includes # stanza is received. Be aware that that includes
# MUC messages and error messages. # MUC messages and error messages.
self.add_event_handler("message", self.process_message) 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. Process the session_start event.
@ -178,176 +81,14 @@ class Slixmark(slixmpp.ClientXMPP):
# await self.get_roster() # await self.get_roster()
await self['xep_0115'].update_caps() await self['xep_0115'].update_caps()
async def discovery(self, DiscoInfo): async def process_disco_info(self, DiscoInfo):
jid = DiscoInfo['from'] jid = DiscoInfo['from']
await self['xep_0115'].update_caps(jid=jid) await self['xep_0115'].update_caps(jid=jid)
# jid_bare = DiscoInfo['from'].bare # jid_bare = DiscoInfo['from'].bare
# self.bookmarks_db = buku.BukuDb(dbfile=jid_bare + '.db') # self.bookmarks_db = buku.BukuDb(dbfile=jid_bare + '.db')
def process_message(self, message): def process_message(self, message):
""" Chat.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 = 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) <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, 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) <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) + '\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
def command_list(self): def command_list(self):
self['xep_0050'].add_command(node='add', self['xep_0050'].add_command(node='add',
@ -412,7 +153,7 @@ class Slixmark(slixmpp.ClientXMPP):
def _handle_edit_single(self, payload, session): def _handle_edit_single(self, payload, session):
jid_bare = session['from'].bare 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 = self['xep_0004'].make_form('form', 'Edit')
form['instructions'] = 'Edit bookmark' form['instructions'] = 'Edit bookmark'
url = payload['values']['url'] url = payload['values']['url']
@ -431,23 +172,23 @@ class Slixmark(slixmpp.ClientXMPP):
bookmark = bookmarks_db.get_rec_by_id(idx) bookmark = bookmarks_db.get_rec_by_id(idx)
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='ID #', label='ID #',
value=str(bookmark.id)) value=str(idx))
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='Title', label='Title',
value=bookmark.title) value=bookmark[2])
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='URL', label='URL',
value=bookmark.url) value=bookmark[1])
form.add_field(ftype='text-multi', form.add_field(ftype='text-multi',
label='Note', label='Note',
value=bookmark.desc) value=bookmark[4])
options = form.add_field(ftype='list-multi', options = form.add_field(ftype='list-multi',
label='Tags') label='Tags')
for tag in bookmark.tags_raw.split(','): for tag in bookmark[3].split(','):
options.addOption(tag, tag) options.addOption(tag, tag)
form.add_field(ftype='boolean', form.add_field(ftype='boolean',
label='Immutable', label='Immutable',
value=str(bookmark.flags)) value=str(bookmark[5]))
session['allow_complete'] = True session['allow_complete'] = True
session['has_next'] = False session['has_next'] = False
session['next'] = None session['next'] = None
@ -479,7 +220,7 @@ class Slixmark(slixmpp.ClientXMPP):
def _handle_tag(self, iq, session): def _handle_tag(self, iq, session):
jid_bare = session['from'].bare 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 = self['xep_0004'].make_form('form', 'Tags')
form['instructions'] = ('Select tags to browse') form['instructions'] = ('Select tags to browse')
options = form.add_field(desc='Select tag to list its items.', 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): def _handle_browse_all(self, payload, session):
jid_bare = session['from'].bare jid_bare = session['from'].bare
bookmarks_db = Actions.init_db(jid_bare) bookmarks_db = Configuration.init_db(jid_bare)
vals = payload['values'] vals = payload['values']
if 'url' in vals and vals['url']: if 'url' in vals and vals['url']:
act = vals['action'] act = vals['action']
@ -515,11 +256,11 @@ class Slixmark(slixmpp.ClientXMPP):
idx = bookmarks_db.get_rec_id(i) idx = bookmarks_db.get_rec_id(i)
idxs += ',' + str(idx) idxs += ',' + str(idx)
bookmark = bookmarks_db.get_rec_by_id(idx) bookmark = bookmarks_db.get_rec_by_id(idx)
tags += bookmark.tags_raw tags += bookmark[3]
form.add_field(desc='Comma-separated tags.', form.add_field(desc='Comma-separated tags.',
ftype='text-single', ftype='text-single',
label='Tags', label='Tags',
value=bookmark.tags_raw, value=bookmark[3],
var='tags_new') var='tags_new')
form.add_field(desc='Check to disable automatic title fetch.', form.add_field(desc='Check to disable automatic title fetch.',
ftype='boolean', ftype='boolean',
@ -536,32 +277,32 @@ class Slixmark(slixmpp.ClientXMPP):
bookmark = bookmarks_db.get_rec_by_id(idx) bookmark = bookmarks_db.get_rec_by_id(idx)
form.add_field(ftype='fixed', form.add_field(ftype='fixed',
label='ID #', label='ID #',
value=str(bookmark.id), value=str(idx),
var='id') var='id')
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='Title', label='Title',
value=bookmark.title, value=bookmark[2],
var='title') var='title')
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='URL', label='URL',
value=bookmark.url, value=bookmark[1],
var='url') var='url')
form.add_field(ftype='text-multi', form.add_field(ftype='text-multi',
label='Note', label='Note',
value=bookmark.desc, value=bookmark[4],
var='description') var='description')
form.add_field(ftype='hidden', form.add_field(ftype='hidden',
value=bookmark.tags_raw, value=bookmark[3],
var='tags_old') var='tags_old')
form.add_field(desc='Comma-separated tags.', form.add_field(desc='Comma-separated tags.',
ftype='text-single', ftype='text-single',
label='Tags', label='Tags',
value=bookmark.tags_raw, value=bookmark[3],
var='tags_new') var='tags_new')
form.add_field(desc='Check to disable automatic title fetch.', form.add_field(desc='Check to disable automatic title fetch.',
ftype='boolean', ftype='boolean',
label='Immutable', label='Immutable',
value=str(bookmark.flags), value=str(bookmark[5]),
var='immutable') var='immutable')
case 'remove': case 'remove':
form['instructions'] = ('The following items were deleted from ' form['instructions'] = ('The following items were deleted from '
@ -607,7 +348,7 @@ class Slixmark(slixmpp.ClientXMPP):
limiter = counter + int(limit) limiter = counter + int(limit)
for bookmark in bookmarks[counter:limiter]: for bookmark in bookmarks[counter:limiter]:
if counter == limiter: break if counter == limiter: break
options.addOption(bookmark.title, bookmark.url) options.addOption(bookmark[2], bookmark[1])
counter += 1 counter += 1
form.add_field(ftype='hidden', form.add_field(ftype='hidden',
value=str(counter), value=str(counter),
@ -622,7 +363,7 @@ class Slixmark(slixmpp.ClientXMPP):
def _handle_action_result(self, payload, session): def _handle_action_result(self, payload, session):
jid_bare = session['from'].bare jid_bare = session['from'].bare
bookmarks_db = Actions.init_db(jid_bare) bookmarks_db = Configuration.init_db(jid_bare)
vals = payload['values'] vals = payload['values']
if 'id' in vals: if 'id' in vals:
idx = vals['id'] idx = vals['id']
@ -688,7 +429,7 @@ class Slixmark(slixmpp.ClientXMPP):
idx = bookmarks_db.add_rec(url=i) idx = bookmarks_db.add_rec(url=i)
rec = bookmarks_db.get_rec_by_id(idx) rec = bookmarks_db.get_rec_by_id(idx)
form.add_field(ftype='fixed', form.add_field(ftype='fixed',
value=str(rec.id)) value=str(idx))
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
value=rec.title) value=rec.title)
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
@ -701,36 +442,36 @@ class Slixmark(slixmpp.ClientXMPP):
def _handle_random(self, iq, session): def _handle_random(self, iq, session):
jid_bare = session['from'].bare 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() bookmarks = bookmarks_db.get_rec_all()
if bookmarks: if bookmarks:
import random import random
bookmark = random.choice(bookmarks) bookmark = random.choice(bookmarks)
form = self['xep_0004'].make_form('form', 'Random') 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', form.add_field(ftype='fixed',
# ftype='text-single', # ftype='text-single',
label='URL', label='URL',
# required=True, # required=True,
value=bookmark.url, value=bookmark[1],
var='url') var='url')
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='Title', label='Title',
value=bookmark.title, value=bookmark[2],
var='title') var='title')
form.add_field(ftype='text-multi', form.add_field(ftype='text-multi',
label='Note', label='Note',
value=bookmark.desc, value=bookmark[4],
var='note') var='note')
form.add_field(desc='Comma-separated tags.', form.add_field(desc='Comma-separated tags.',
ftype='text-single', ftype='text-single',
label='Tags', label='Tags',
value=bookmark.tags_raw, value=bookmark[3],
var='tag') var='tag')
form.add_field(desc='Check to disable automatic title fetch.', form.add_field(desc='Check to disable automatic title fetch.',
ftype='boolean', ftype='boolean',
label='Immutable', label='Immutable',
value=str(bookmark.flags), value=str(bookmark[5]),
var='immutable') var='immutable')
session['has_next'] = True session['has_next'] = True
session['next'] = self._handle_edit_single session['next'] = self._handle_edit_single
@ -773,7 +514,7 @@ class Slixmark(slixmpp.ClientXMPP):
def _handle_search_result(self, payload, session): def _handle_search_result(self, payload, session):
jid_bare = session['from'].bare jid_bare = session['from'].bare
bookmarks_db = Actions.init_db(jid_bare) bookmarks_db = Configuration.init_db(jid_bare)
vals = payload['values'] vals = payload['values']
if 'url' in vals and vals['url']: if 'url' in vals and vals['url']:
act = vals['action'] act = vals['action']
@ -789,11 +530,11 @@ class Slixmark(slixmpp.ClientXMPP):
idx = bookmarks_db.get_rec_id(i) idx = bookmarks_db.get_rec_id(i)
idxs += ',' + str(idx) idxs += ',' + str(idx)
bookmark = bookmarks_db.get_rec_by_id(idx) bookmark = bookmarks_db.get_rec_by_id(idx)
tags += bookmark.tags_raw tags += bookmark[3]
form.add_field(desc='Comma-separated tags.', form.add_field(desc='Comma-separated tags.',
ftype='text-single', ftype='text-single',
label='Tags', label='Tags',
value=bookmark.tags_raw, value=bookmark[3],
var='tags_new') var='tags_new')
form.add_field(desc='Check to disable automatic title fetch.', form.add_field(desc='Check to disable automatic title fetch.',
ftype='boolean', ftype='boolean',
@ -810,32 +551,32 @@ class Slixmark(slixmpp.ClientXMPP):
bookmark = bookmarks_db.get_rec_by_id(idx) bookmark = bookmarks_db.get_rec_by_id(idx)
form.add_field(ftype='fixed', form.add_field(ftype='fixed',
label='ID #', label='ID #',
value=str(bookmark.id), value=str(idx),
var='id') var='id')
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='Title', label='Title',
value=bookmark.title, value=bookmark[2],
var='title') var='title')
form.add_field(ftype='text-single', form.add_field(ftype='text-single',
label='URL', label='URL',
value=bookmark.url, value=bookmark[1],
var='url') var='url')
form.add_field(ftype='text-multi', form.add_field(ftype='text-multi',
label='Note', label='Note',
value=bookmark.desc, value=bookmark[4],
var='description') var='description')
form.add_field(ftype='hidden', form.add_field(ftype='hidden',
value=bookmark.tags_raw, value=bookmark[3],
var='tags_old') var='tags_old')
form.add_field(desc='Comma-separated tags.', form.add_field(desc='Comma-separated tags.',
ftype='text-single', ftype='text-single',
label='Tags', label='Tags',
value=bookmark.tags_raw, value=bookmark[3],
var='tags_new') var='tags_new')
form.add_field(desc='Check to disable automatic title fetch.', form.add_field(desc='Check to disable automatic title fetch.',
ftype='boolean', ftype='boolean',
label='Immutable', label='Immutable',
value=str(bookmark.flags), value=str(bookmark[5]),
var='immutable') var='immutable')
case 'remove': case 'remove':
form['instructions'] = ('The following items were deleted from ' form['instructions'] = ('The following items were deleted from '
@ -903,7 +644,7 @@ class Slixmark(slixmpp.ClientXMPP):
limiter = counter + 10 limiter = counter + 10
for bookmark in bookmarks[counter:limiter]: for bookmark in bookmarks[counter:limiter]:
if counter == limiter: break if counter == limiter: break
options.addOption(bookmark.title, bookmark.url) options.addOption(bookmark[2], bookmark[1])
counter += 1 counter += 1
form.add_field(ftype='hidden', form.add_field(ftype='hidden',
value=str(counter), value=str(counter),
@ -955,47 +696,3 @@ class Slixmark(slixmpp.ClientXMPP):
return session 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()