forked from sch/Slixfeed
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:
parent
c9c552e33f
commit
2f6b86522f
16 changed files with 548 additions and 118 deletions
|
@ -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.
|
||||
|
|
|
@ -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__":
|
||||
|
|
|
@ -123,17 +123,22 @@ def is_feed_json(document):
|
|||
True or False.
|
||||
"""
|
||||
value = False
|
||||
feed = json.loads(document)
|
||||
if not feed['items']:
|
||||
if "version" in feed.keys():
|
||||
if 'jsonfeed' in feed['version']:
|
||||
value = True
|
||||
# elif 'title' in feed.keys():
|
||||
# value = True
|
||||
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 = False
|
||||
else:
|
||||
value = True
|
||||
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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
284
slixfeed/xmpp/component.py
Normal 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
|
||||
)
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,48 +19,54 @@ async def request(self, jid):
|
|||
None.
|
||||
"""
|
||||
# Check whether JID is subscribed; otherwise, ask for presence.
|
||||
if not self.client_roster[jid]["to"]:
|
||||
self.send_presence_subscription(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
ptype="subscribe",
|
||||
pnick=self.nick
|
||||
)
|
||||
self.send_message(
|
||||
mto=jid,
|
||||
# mtype="headline",
|
||||
msubject="RSS News Bot",
|
||||
mbody=(
|
||||
"Share online status to receive updates."
|
||||
),
|
||||
mfrom=self.boundjid.bare,
|
||||
mnick=self.nick
|
||||
)
|
||||
self.send_presence(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
# Accept symbol 🉑️ 👍️ ✍
|
||||
pstatus=(
|
||||
"✒️ Share online status to receive updates."
|
||||
),
|
||||
# ptype="subscribe",
|
||||
pnick=self.nick
|
||||
)
|
||||
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.alias
|
||||
)
|
||||
self.send_message(
|
||||
mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
# mtype="headline",
|
||||
msubject="RSS News Bot",
|
||||
mbody=(
|
||||
"Share online status to receive updates."
|
||||
),
|
||||
mnick=self.alias
|
||||
)
|
||||
self.send_presence(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
# Accept symbol 🉑️ 👍️ ✍
|
||||
pstatus=(
|
||||
"✒️ Share online status to receive updates."
|
||||
),
|
||||
# ptype="subscribe",
|
||||
pnick=self.alias
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue