Add service component support.

Thank you to Mr. Guus der Kinderen from IgniteRealtime.org for promptly providing an Openfire instance for development.
This commit is contained in:
Schimon Jehudah 2024-01-24 18:11:39 +00:00
parent c9c552e33f
commit 2f6b86522f
16 changed files with 548 additions and 118 deletions

View file

@ -1,9 +1,13 @@
Thank you goes to the following kind people.
The following people have been involved in this project by consulting, developing and testing.
[Alixander Court](https://alixandercourt.com/)
[edhelas](https://github.com/edhelas/atomtopubsub)
Guus der Kinderen (promptly dedicating a personal [Openfire](https://www.igniterealtime.org/projects/) server for further developments and tests)
[habnabit_](irc://irc.libera.chat/#python) (SQL security)
[imattau](https://github.com/imattau/atomtopubsub) (Some code, mostly URL handling, was taken from imattau)
@ -16,4 +20,4 @@ Simone "roughnecks" Canaletti
Strix from Loqi
Slixmpp participants who chose to remain anonymous or not to appear in this list.
Slixmpp participants amf #python members who explicitly chose to remain anonymous or not to appear in this list.

View file

@ -96,21 +96,54 @@ import os
# # with start_action(action_type="set_date()", jid=jid):
# # with start_action(action_type="message()", msg=msg):
#import slixfeed.irchandler
#import slixfeed.smtp
#import slixfeed.irc
#import slixfeed.matrix
from slixfeed.config import get_value
from slixfeed.xmpp.client import Slixfeed
#import slixfeed.matrixhandler
import socks
import socket
xmpp_type = get_value(
"accounts", "XMPP", "type")
class Jabber:
def __init__(self, jid, password, nick):
match xmpp_type:
case "client":
from slixfeed.xmpp.client import Slixfeed
case "component":
from slixfeed.xmpp.component import Slixfeed
class JabberComponent:
def __init__(self, jid, secret, hostname, port, alias):
xmpp = Slixfeed(jid, secret, hostname, port, alias)
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat
# xmpp.register_plugin('xep_0048') # Bookmarks
xmpp.register_plugin('xep_0054') # vcard-temp
xmpp.register_plugin('xep_0060') # Publish-Subscribe
# xmpp.register_plugin('xep_0065') # SOCKS5 Bytestreams
xmpp.register_plugin('xep_0066') # Out of Band Data
xmpp.register_plugin('xep_0071') # XHTML-IM
xmpp.register_plugin('xep_0084') # User Avatar
# xmpp.register_plugin('xep_0085') # Chat State Notifications
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
xmpp.register_plugin('xep_0249') # Multi-User Chat
xmpp.register_plugin('xep_0363') # HTTP File Upload
xmpp.register_plugin('xep_0402') # PEP Native Bookmarks
xmpp.connect()
xmpp.process()
class JabberClient:
def __init__(self, jid, password, alias):
# Setup the Slixfeed and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = Slixfeed(jid, password, nick)
xmpp = Slixfeed(jid, password, alias)
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat
@ -128,9 +161,9 @@ class Jabber:
xmpp.register_plugin('xep_0363') # HTTP File Upload
xmpp.register_plugin('xep_0402') # PEP Native Bookmarks
# proxy_enabled = get_value("accounts", "XMPP Connect", "proxy_enabled")
# proxy_enabled = get_value("accounts", "XMPP", "proxy_enabled")
# if proxy_enabled == '1':
# values = get_value("accounts", "XMPP Connect", [
# values = get_value("accounts", "XMPP", [
# "proxy_host",
# "proxy_port",
# "proxy_username",
@ -150,7 +183,7 @@ class Jabber:
# Connect to the XMPP server and start processing XMPP stanzas.
address = get_value(
"accounts", "XMPP", ["address", "port"])
"accounts", "XMPP Client", ["hostname", "port"])
if address[0] and address[1]:
xmpp.connect(tuple(address))
else:
@ -189,7 +222,11 @@ def main():
parser.add_argument(
"-p", "--password", dest="password", help="Password of JID")
parser.add_argument(
"-n", "--nickname", dest="nickname", help="Display name")
"-a", "--alias", dest="alias", help="Display name")
parser.add_argument(
"-n", "--hostname", dest="hostname", help="Hostname")
parser.add_argument(
"-o", "--port", dest="port", help="Port number")
args = parser.parse_args()
@ -199,28 +236,39 @@ def main():
# Try configuration file
values = get_value(
"accounts", "XMPP", ["nickname", "username", "password"])
nickname = values[0]
"accounts", "XMPP Client", [
"alias", "username", "password", "hostname", "port"])
alias = values[0]
username = values[1]
password = values[2]
hostname = values[3]
port = values[4]
# Use arguments if were given
if args.jid:
username = args.jid
if args.password:
password = args.password
if args.nickname:
nickname = args.nickname
if args.alias:
alias = args.alias
if args.hostname:
hostname = args.hostname
if args.port:
port = args.port
# Prompt for credentials if none were given
if not username:
username = input("Username: ")
if not password:
password = getpass("Password: ")
if not nickname:
nickname = input("Nickname: ")
if not alias:
alias = (input("Alias: ")) or "Slixfeed"
Jabber(username, password, nickname)
match xmpp_type:
case "client":
JabberClient(username, password, alias)
case "component":
JabberComponent(username, password, hostname, port, alias)
sys.exit(0)
if __name__ == "__main__":

View file

@ -123,17 +123,22 @@ def is_feed_json(document):
True or False.
"""
value = False
try:
feed = json.loads(document)
if not feed['items']:
if "version" in feed.keys():
if 'jsonfeed' in feed['version']:
value = True
else: # TODO Test
value = False
# elif 'title' in feed.keys():
# value = True
else:
value = False
else:
value = True
except:
pass
return value
@ -1039,8 +1044,6 @@ def generate_document(data, url, ext, filename):
"are installed, or try again.")
error = (
"Package pdfkit or wkhtmltopdf was not found.")
case "text":
generate_txt(content, filename)
case "txt":
generate_txt(content, filename)
if error:

View file

@ -2,36 +2,45 @@
# and also from which accounts it receives instructions.
[XMPP]
nickname = Slixfeed
reconnect_timeout = 30
type = client
#type = component
[XMPP Client]
alias = Slixfeed
username =
password =
# JID of bot master
operator =
# Address may be an onion hostname
address =
# Hostname (also address) may be an i2p or onion hostname
hostname =
# Port may be 5347
port =
[XMPP Component]
alias = Slixfeed
username =
password =
hostname =
port =
[XMPP Profile]
name = Slixfeed
nickname = Slixfeed
alias = Slixfeed
role = Syndication News Bot
organization = RSS Task Force
url = https://gitgud.io/sjehuda/slixfeed
description = XMPP news bot (supports Atom, JSON, RDF and RSS).
note = This is a syndication news bot powered by Slixfeed.
birthday = 21 June 2022
# JID of bot master
operator =
[XMPP Connect]
reconnect_timeout = 30
[XMPP Proxy]
# NOTE You might want to consider support for socks4 too (this
# note was written when keys were proxy_host and proxy_port)
# NOTE Consider not to use a version number as it might give an
# impression of an archaic feature in the future.
[XMPP Proxy]
# Example hostname 127.0.0.1
socks5_host =
# Example port 9050

View file

@ -413,7 +413,7 @@ proxies:
- https://quetre.projectsegfau.lt
- https://quetre.esmailelbob.xyz
- https://quetre.odyssey346.dev
- ://ask.habedieeh.re
- https://ask.habedieeh.re
- https://quetre.marcopisco.com
- https://quetre.blackdrgn.nl
- https://quetre.lunar.icu

View file

@ -17,6 +17,8 @@ TODO
5) Merge get_value_default into get_value.
6) Use TOML https://ruudvanasseldonk.com/2023/01/11/the-yaml-document-from-hell
"""
import configparser

View file

@ -35,7 +35,7 @@ NOTE
check_readiness
<presence from="slixfeed@canchat.org/xAPgJLHtMMHF" xml:lang="en" id="ab35c07b63a444d0a7c0a9a0b272f301" to="slixfeed@canchat.org/xAPgJLHtMMHF"><status>📂 Send a URL from a blog or a news website.</status><x xmlns="vcard-temp:x:update"><photo /></x></presence>
JID: self.boundjid.bare
MUC: self.nick
MUC: self.alias
"""
@ -258,11 +258,21 @@ async def send_update(self, jid, num=None):
if media and news_digest:
# Send textual message
xmpp.Slixfeed.send_message(
self, mto=jid, mbody=news_digest, mtype=chat_type)
self,
mto=jid,
mfrom=self.boundjid.bare,
mbody=news_digest,
mtype=chat_type
)
news_digest = ''
# Send media
message = xmpp.Slixfeed.make_message(
self, mto=jid, mbody=media, mtype=chat_type)
self,
mto=jid,
mfrom=self.boundjid.bare,
mbody=media,
mtype=chat_type
)
message['oob']['url'] = media
message.send()
media = None
@ -274,7 +284,12 @@ async def send_update(self, jid, num=None):
if chat_type in ("chat", "groupchat"):
# TODO Provide a choice (with or without images)
xmpp.Slixfeed.send_message(
self, mto=jid, mbody=news_digest, mtype=chat_type)
self,
mto=jid,
mfrom=self.boundjid.bare,
mbody=news_digest,
mtype=chat_type
)
# if media:
# # message = xmpp.Slixfeed.make_message(
# # self, mto=jid, mbody=new, mtype=chat_type)
@ -365,10 +380,10 @@ async def send_status(self, jid):
# print(await current_time(), status_text, "for", jid)
xmpp.Slixfeed.send_presence(
self,
pshow=status_mode,
pstatus=status_text,
pto=jid,
#pfrom=None
pfrom=self.boundjid.bare,
pshow=status_mode,
pstatus=status_text
)
# await asyncio.sleep(60 * 20)
await refresh_task(

View file

@ -19,7 +19,7 @@ async def add(self, muc_jid):
for muc in mucs:
bookmarks.add_conference(
muc,
self.nick,
self.alias,
autojoin=True
)
await self.plugin['xep_0048'].set_bookmarks(bookmarks)
@ -28,7 +28,7 @@ async def add(self, muc_jid):
# print(await self.plugin['xep_0048'].get_bookmarks())
# bm = BookmarkStorage()
# bm.conferences.append(Conference(muc_jid, autojoin=True, nick=self.nick))
# bm.conferences.append(Conference(muc_jid, autojoin=True, nick=self.alias))
# await self['xep_0402'].publish(bm)
@ -53,7 +53,7 @@ async def remove(self, muc_jid):
for muc in mucs:
bookmarks.add_conference(
muc,
self.nick,
self.alias,
autojoin=True
)
await self.plugin['xep_0048'].set_bookmarks(bookmarks)

View file

@ -35,7 +35,7 @@ NOTE
check_readiness
📂 Send a URL from a blog or a news website.
JID: self.boundjid.bare
MUC: self.nick
MUC: self.alias
2) Extracting attribute using xmltodict.
import xmltodict
@ -91,13 +91,13 @@ class Slixfeed(slixmpp.ClientXMPP):
-------
News bot that sends updates from RSS feeds.
"""
def __init__(self, jid, password, nick):
def __init__(self, jid, password, alias):
slixmpp.ClientXMPP.__init__(self, jid, password)
# NOTE
# The bot works fine when the nickname is hardcoded; or
# The bot won't join some MUCs when its nickname has brackets
self.nick = nick
self.alias = alias
# 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
@ -158,24 +158,24 @@ class Slixfeed(slixmpp.ClientXMPP):
async def on_session_end(self, event):
message = "Session has ended."
message = "Session has ended. Reason: {}".format(event)
await connect.recover_connection(self, event, message)
async def on_connection_failed(self, event):
message = "Connection has failed."
message = "Connection has failed. Reason: {}".format(event)
await connect.recover_connection(self, event, message)
async def on_session_start(self, event):
await process.event(self, event)
await muc.autojoin(self, event)
await muc.autojoin(self)
await profile.update(self)
async def on_session_resumed(self, event):
await process.event(self, event)
await muc.autojoin(self, event)
await muc.autojoin(self)
# TODO Request for subscription
@ -196,10 +196,10 @@ class Slixfeed(slixmpp.ClientXMPP):
# TODO Request for subscription
async def on_presence_subscribe(self, presence):
jid = presence["from"].bare
await state.request(self, jid)
print("on_presence_subscribe")
print(presence)
jid = presence["from"].bare
await state.request(self, jid)
async def on_presence_subscribed(self, presence):
@ -214,6 +214,8 @@ class Slixfeed(slixmpp.ClientXMPP):
async def on_presence_unsubscribed(self, presence):
await state.unsubscribed(self, presence)
jid = presence["from"].bare
await roster.remove(self, jid)
async def on_presence_unavailable(self, presence):

284
slixfeed/xmpp/component.py Normal file
View file

@ -0,0 +1,284 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FIXME
1) Function check_readiness or event "changed_status" is causing for
triple status messages and also false ones that indicate of lack
of feeds.
TODO
1) Use loop (with gather) instead of TaskGroup.
2) Assure message delivery before calling a new task.
See https://slixmpp.readthedocs.io/en/latest/event_index.html#term-marker_acknowledged
3) XHTTML-IM
case _ if message_lowercase.startswith("html"):
message['html']="
Parse me!
"
self.send_message(
mto=jid,
mfrom=self.boundjid.bare,
mhtml=message
)
NOTE
1) Self presence
Apparently, it is possible to view self presence.
This means that there is no need to store presences in order to switch or restore presence.
check_readiness
📂 Send a URL from a blog or a news website.
JID: self.boundjid.bare
MUC: self.alias
2) Extracting attribute using xmltodict.
import xmltodict
message = xmltodict.parse(str(message))
jid = message["message"]["x"]["@jid"]
"""
import asyncio
import logging
# import os
from random import randrange
import slixmpp
import slixfeed.task as task
from time import sleep
from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound
# from slixmpp.plugins.xep_0402 import BookmarkStorage, Conference
from slixmpp.plugins.xep_0048.stanza import Bookmarks
# import xmltodict
# import xml.etree.ElementTree as ET
# from lxml import etree
import slixfeed.xmpp.connect as connect
import slixfeed.xmpp.muc as muc
import slixfeed.xmpp.process as process
import slixfeed.xmpp.profile as profile
import slixfeed.xmpp.roster as roster
import slixfeed.xmpp.state as state
import slixfeed.xmpp.status as status
import slixfeed.xmpp.utility as utility
main_task = []
jid_tasker = {}
task_manager = {}
loop = asyncio.get_event_loop()
# asyncio.set_event_loop(loop)
# time_now = datetime.now()
# time_now = time_now.strftime("%H:%M:%S")
# def print_time():
# # return datetime.now().strftime("%H:%M:%S")
# now = datetime.now()
# current_time = now.strftime("%H:%M:%S")
# return current_time
class Slixfeed(slixmpp.ComponentXMPP):
"""
Slixmpp
-------
News bot that sends updates from RSS feeds.
"""
def __init__(self, jid, secret, hostname, port, alias):
slixmpp.ComponentXMPP.__init__(self, jid, secret, hostname, port)
# 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.on_session_start)
self.add_event_handler("session_resumed", self.on_session_resumed)
self.add_event_handler("got_offline", print("got_offline"))
# self.add_event_handler("got_online", self.check_readiness)
self.add_event_handler("changed_status", self.on_changed_status)
self.add_event_handler("presence_available", self.on_presence_available)
self.add_event_handler("presence_unavailable", self.on_presence_unavailable)
self.add_event_handler("changed_subscription", self.on_changed_subscription)
self.add_event_handler("chatstate_active", self.on_chatstate_active)
self.add_event_handler("chatstate_gone", self.on_chatstate_gone)
self.add_event_handler("chatstate_composing", self.check_chatstate_composing)
self.add_event_handler("chatstate_paused", self.check_chatstate_paused)
# 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.on_message)
self.add_event_handler("groupchat_invite", self.on_groupchat_invite) # XEP_0045
self.add_event_handler("groupchat_direct_invite", self.on_groupchat_direct_invite) # XEP_0249
# self.add_event_handler("groupchat_message", self.message)
# self.add_event_handler("disconnected", self.reconnect)
# self.add_event_handler("disconnected", self.inspect_connection)
self.add_event_handler("reactions", self.on_reactions)
self.add_event_handler("presence_error", self.on_presence_error)
self.add_event_handler("presence_subscribe", self.on_presence_subscribe)
self.add_event_handler("presence_subscribed", self.on_presence_subscribed)
self.add_event_handler("presence_unsubscribe", self.on_presence_unsubscribe)
self.add_event_handler("presence_unsubscribed", self.on_presence_unsubscribed)
# Initialize event loop
# self.loop = asyncio.get_event_loop()
# handlers for connection events
self.connection_attempts = 0
self.max_connection_attempts = 10
self.add_event_handler("connection_failed", self.on_connection_failed)
self.add_event_handler("session_end", self.on_session_end)
async def on_groupchat_invite(self, message):
print("on_groupchat_invite")
await muc.accept_invitation(self, message)
async def on_groupchat_direct_invite(self, message):
print("on_groupchat_direct_invite")
await muc.accept_invitation(self, message)
async def on_session_end(self, event):
message = "Session has ended. Reason: {}".format(event)
await connect.recover_connection(self, event, message)
async def on_connection_failed(self, event):
message = "Connection has failed. Reason: {}".format(event)
await connect.recover_connection(self, event, message)
async def on_session_start(self, event):
await process.event_component(self, event)
# await muc.autojoin(self)
await profile.update(self)
async def on_session_resumed(self, event):
await process.event_component(self, event)
# await muc.autojoin(self)
# TODO Request for subscription
async def on_message(self, message):
# print(message)
# breakpoint()
jid = message["from"].bare
# if "chat" == await utility.jid_type(self, jid):
# await roster.add(self, jid)
# await state.request(self, jid)
# chat_type = message["type"]
# message_body = message["body"]
# message_reply = message.reply
await process.message(self, message)
async def on_changed_status(self, presence):
await task.check_readiness(self, presence)
# TODO Request for subscription
async def on_presence_subscribe(self, presence):
print("on_presence_subscribe")
print(presence)
jid = presence["from"].bare
# await state.request(self, jid)
self.send_presence_subscription(
pto=jid,
pfrom=self.boundjid.bare,
ptype="subscribe",
pnick=self.alias
)
async def on_presence_subscribed(self, presence):
jid = presence["from"].bare
process.greet(self, jid)
async def on_presence_available(self, presence):
# TODO Add function to check whether task is already running or not
await task.start_tasks(self, presence)
async def on_presence_unsubscribed(self, presence):
await state.unsubscribed(self, presence)
async def on_presence_unavailable(self, presence):
await task.stop_tasks(self, presence)
async def on_changed_subscription(self, presence):
print("on_changed_subscription")
print(presence)
jid = presence["from"].bare
# breakpoint()
async def on_presence_unsubscribe(self, presence):
print("on_presence_unsubscribe")
print(presence)
async def on_presence_error(self, presence):
print("on_presence_error")
print(presence)
async def on_reactions(self, message):
print("on_reactions")
print(message)
async def on_chatstate_active(self, message):
print("on_chatstate_active")
print(message)
async def on_chatstate_gone(self, message):
print("on_chatstate_gone")
print(message)
async def check_chatstate_composing(self, message):
print("def check_chatstate_composing")
print(message)
if message["type"] in ("chat", "normal"):
jid = message["from"].bare
status_text="Press \"help\" for manual."
self.send_presence(
# pshow=status_mode,
pstatus=status_text,
pto=jid,
)
async def check_chatstate_paused(self, message):
print("def check_chatstate_paused")
print(message)
if message["type"] in ("chat", "normal"):
jid = message["from"].bare
await task.refresh_task(
self,
jid,
task.send_status,
"status",
20
)

View file

@ -1,6 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TODO
1) Check interval, and if no connection is establish after 30 seconds
then disconnect and reconnect again.
2) or Check ping, and if no ping is received after 30 seconds then
disconnect and try to reconnect again.
"""
from slixfeed.config import get_value
from slixfeed.dt import current_time
from time import sleep
@ -18,7 +30,7 @@ async def recover_connection(self, event, message):
# logging.error("Maximum connection attempts exceeded.")
print(current_time(), "Attempt number", self.connection_attempts)
seconds = (get_value(
"accounts", "XMPP Connect", "reconnect_timeout")) or 30
"accounts", "XMPP", "reconnect_timeout")) or 30
seconds = int(seconds)
print(current_time(), "Next attempt within", seconds, "seconds")
# NOTE asyncio.sleep doesn't interval as expected

View file

@ -40,7 +40,7 @@ def print_info():
" Supported protocols: Dat, FTP, Gemini, Gopher, HTTP and IPFS.\n"
"\n"
"AUTHORS\n"
" Laura Harbinger, Schimon Zackary.\n"
" Laura Lapina, Schimon Zackary.\n"
"\n"
"THANKS\n"
" Christian Dersch (SalixOS),"
@ -53,15 +53,19 @@ def print_info():
" Florent Le Coz (poezio, France),"
"\n"
" George Vlahavas (SalixOS, Greece),"
" Guus der Kinderen (IgniteRealtime.org Openfire, Netherlands),"
"\n"
" Maxime Buquet (slixmpp, France),"
"\n"
" Mathieu Pasquet (slixmpp, France),"
" Pierrick Le Brun (SalixOS, France),"
"\n"
" Pierrick Le Brun (SalixOS, France),"
" Remko Tronçon (Swift, Germany),"
"\n"
" Raphael Groner (Fedora, Germany),"
" Thorsten Mühlfelder (SalixOS, Germany),"
"\n"
" Yann Leboulanger (Gajim, France).\n"
" Yann Leboulanger (Gajim, France)."
"\n"
"\n"
"COPYRIGHT\n"
" Slixfeed is free software; you can redistribute it and/or\n"
@ -70,7 +74,7 @@ def print_info():
" Slixfeed is distributed in the hope that it will be useful,\n"
" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
" GNU General Public License for more details.\n"
" MIT License for more details.\n"
"\n"
"NOTE\n"
" You can run Slixfeed on your own computer, server, and\n"

View file

@ -31,17 +31,20 @@ async def accept_invitation(self, message):
await join(self, inviter, muc_jid)
async def autojoin(self, event):
async def autojoin(self):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result["private"]["bookmarks"]
conferences = bookmarks["conferences"]
for conference in conferences:
if conference["autojoin"]:
muc_jid = conference["jid"]
logging.debug("Autojoin groupchat", muc_jid)
logging.debug(
"Autojoin {} ({})".format(conference["name"], muc_jid))
print(
"Autojoin {} ({})".format(conference["name"], muc_jid))
self.plugin['xep_0045'].join_muc(
muc_jid,
self.nick,
conference["nick"],
# If a room password is needed, use:
# password=the_room_password,
)
@ -61,6 +64,7 @@ async def join(self, inviter, muc_jid):
# )
# self.send_message(
# mto=inviter,
# mfrom=self.boundjid.bare,
# mbody=(
# "Send activation token {} to groupchat xmpp:{}?join."
# ).format(token, muc_jid)
@ -69,7 +73,7 @@ async def join(self, inviter, muc_jid):
print(muc_jid)
self.plugin['xep_0045'].join_muc(
muc_jid,
self.nick,
self.alias,
# If a room password is needed, use:
# password=the_room_password,
)
@ -87,13 +91,14 @@ async def leave(self, muc_jid):
for message in messages:
self.send_message(
mto=muc_jid,
mfrom=self.boundjid.bare,
mbody=message,
mtype="groupchat"
)
await bookmark.remove(self, muc_jid)
self.plugin['xep_0045'].leave_muc(
muc_jid,
self.nick,
self.alias,
"Goodbye!",
self.boundjid.bare
)

View file

@ -41,6 +41,10 @@ import slixfeed.xmpp.upload as upload
from slixfeed.xmpp.utility import jid_type
async def event_component(self, event):
self.send_presence()
async def event(self, event):
"""
Process the session_start event.
@ -84,12 +88,12 @@ async def message(self, message):
message_text = " ".join(message["body"].split())
# if (message["type"] == "groupchat" and
# message['muc']['nick'] == self.nick):
# message['muc']['nick'] == self.alias):
# return
# FIXME Code repetition. See below.
if message["type"] == "groupchat":
if (message['muc']['nick'] == self.nick):
if (message['muc']['nick'] == self.alias):
return
jid_full = str(message["from"])
role = self.plugin['xep_0045'].get_jid_property(
@ -136,7 +140,7 @@ async def message(self, message):
# nick = message["from"][message["from"].index("/")+1:]
# nick = str(message["from"])
# nick = nick[nick.index("/")+1:]
if (message['muc']['nick'] == self.nick or
if (message['muc']['nick'] == self.alias or
not message["body"].startswith("!")):
return
# token = await initdb(
@ -198,7 +202,7 @@ async def message(self, message):
response = None
match message_lowercase:
# case "breakpoint":
# if jid == get_value("accounts", "XMPP", "operator"):
# if jid == get_value("accounts", "XMPP Profile", "operator"):
# breakpoint()
# print("task_manager[jid]")
# print(task_manager[jid])
@ -368,7 +372,7 @@ async def message(self, message):
send_reply_message(self, message, response)
case _ if message_lowercase.startswith("bookmark -"):
if jid == get_value(
"accounts", "XMPP", "operator"):
"accounts", "XMPP Profile", "operator"):
muc_jid = message_text[11:]
await bookmark.remove(self, muc_jid)
response = (
@ -382,7 +386,7 @@ async def message(self, message):
send_reply_message(self, message, response)
case "bookmarks":
if jid == get_value(
"accounts", "XMPP", "operator"):
"accounts", "XMPP Profile", "operator"):
response = await action.list_bookmarks(self)
else:
response = (
@ -485,7 +489,13 @@ async def message(self, message):
ext = ext if ext else 'pdf'
url = None
error = None
if ext in ("epub", "html", "md", "pdf", "txt"):
if ext in (
"epub", "html", "markdown", "md", "pdf", "text", "txt"):
match ext:
case "markdown":
ext = "md"
case "text":
ext = "txt"
status_type = "dnd"
status_message = (
"📃️ Procesing request to produce {} document..."
@ -504,7 +514,7 @@ async def message(self, message):
try:
url = sqlite.get_entry_url(db_file, ix)
except:
response = "No entry with Id {}".format(ix)
response = "No entry with index {}".format(ix)
except:
url = ix_url
if url:
@ -539,7 +549,7 @@ async def message(self, message):
await task.start_tasks_xmpp(
self, jid, ["status"])
else:
response = "Missing entry Id."
response = "Missing entry index number."
else:
response = "Unsupported filetype."
if response:
@ -859,7 +869,7 @@ async def message(self, message):
).format(url, ix)
except:
response = (
"No news source with ID {}."
"No news source with index {}."
).format(ix)
except:
url = ix_url
@ -878,7 +888,7 @@ async def message(self, message):
await task.clean_tasks_xmpp(jid, ["status"])
await task.start_tasks_xmpp(self, jid, ["status"])
else:
response = "Missing feed ID."
response = "Missing feed index number."
send_reply_message(self, message, response)
case _ if message_lowercase.startswith("reset"):
url = message_text[6:]
@ -947,7 +957,7 @@ async def message(self, message):
"Updates are now disabled for news source {}."
).format(ix)
except:
response = "No news source with ID {}.".format(ix)
response = "No news source with index {}.".format(ix)
send_reply_message(self, message, response)
case _ if message_lowercase.startswith("enable"):
ix = message_text[7:]
@ -958,7 +968,7 @@ async def message(self, message):
"Updates are now enabled for news source {}."
).format(ix)
except:
response = "No news source with ID {}.".format(ix)
response = "No news source with index {}.".format(ix)
send_reply_message(self, message, response)
case "stop":
# FIXME
@ -1067,6 +1077,7 @@ async def send_oob_message(self, jid, url):
f'<a href="{url}">{url}</a></body>')
message = self.make_message(
mto=jid,
mfrom=self.boundjid.bare,
mbody=url,
mhtml=html,
mtype=chat_type
@ -1087,6 +1098,7 @@ async def send_oob_message(self, jid, url):
# for message in messages:
# self.send_message(
# mto=jid,
# mfrom=self.boundjid.bare,
# mbody=message,
# mtype=chat_type
# )
@ -1099,11 +1111,12 @@ def greet(self, jid, chat_type="chat"):
"My job is to bring you the latest "
"news from sources you provide me with.\n"
"You may always reach me via xmpp:{}?message").format(
self.nick,
self.alias,
self.boundjid.bare
)
self.send_message(
mto=jid,
mfrom=self.boundjid.bare,
mbody=message,
mtype=chat_type
)

View file

@ -12,6 +12,25 @@ TODO
import slixfeed.xmpp.utility as utility
async def remove(self, jid):
"""
Remove JID to roster.
Parameters
----------
jid : str
Jabber ID.
Returns
-------
None.
"""
self.update_roster(
jid,
subscription="remove"
)
async def add(self, jid):
"""
Add JID to roster.
@ -35,8 +54,9 @@ async def add(self, jid):
if jid not in self.client_roster.keys():
self.send_presence_subscription(
pto=jid,
pfrom=self.boundjid.bare,
ptype="subscribe",
pnick=self.nick
pnick=self.alias
)
self.update_roster(
jid,

View file

@ -1,6 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from slixmpp.xmlstream.matcher import MatchXPath
from slixmpp.xmlstream.handler import Callback
from slixmpp.xmlstream import ET
async def request(self, jid):
"""
@ -16,22 +19,31 @@ async def request(self, jid):
None.
"""
# Check whether JID is subscribed; otherwise, ask for presence.
if self.is_component:
presence_probe = ET.Element('presence')
presence_probe.attrib['type'] = 'probe'
presence_probe.attrib['to'] = jid
print(presence_probe)
breakpoint()
self.send_raw(str(presence_probe))
presence_probe.send()
else:
if not self.client_roster[jid]["to"]:
self.send_presence_subscription(
pto=jid,
pfrom=self.boundjid.bare,
ptype="subscribe",
pnick=self.nick
pnick=self.alias
)
self.send_message(
mto=jid,
mfrom=self.boundjid.bare,
# mtype="headline",
msubject="RSS News Bot",
mbody=(
"Share online status to receive updates."
),
mfrom=self.boundjid.bare,
mnick=self.nick
mnick=self.alias
)
self.send_presence(
pto=jid,
@ -41,7 +53,7 @@ async def request(self, jid):
"✒️ Share online status to receive updates."
),
# ptype="subscribe",
pnick=self.nick
pnick=self.alias
)
@ -49,15 +61,12 @@ async def unsubscribed(self, presence):
jid = presence["from"].bare
self.send_message(
mto=jid,
mfrom=self.boundjid.bare,
mbody="You have been unsubscribed."
)
self.send_presence(
pto=jid,
pfrom=self.boundjid.bare,
pstatus="🖋️ Subscribe to receive updates",
pnick=self.nick
)
self.update_roster(
jid,
subscription="remove"
pnick=self.alias
)