#!/usr/bin/env python3 # -*- coding: utf-8 -*- # file: idlebot.py # date: 23.07.2020 # desc: class to deal with server related jabber events and muc-offline # presences. import time import random import logging import slixmpp logging = logging.getLogger(__name__) class IdleBot(slixmpp.ClientXMPP): """ Connect the given server, logs in and join the given room. If lost connection to server it tryes to reconnect. """ def __init__(self, jid, password, room, nick): slixmpp.ClientXMPP.__init__(self, jid, password) self.nick = nick self.room = room self.room_roster = {} self.add_event_handler("session_start", self.start) self.add_event_handler("session_end", self.reconnect) self.add_event_handler("disconnected", self.reconnect) self.add_event_handler("muc::%s::got_online" % self.room, self.muc_online) self.add_event_handler("muc::%s::got_offline" % room, self.muc_offline) async def start(self, event): """ Process the session_start event. Typical actions for the session_start event are requesting the roster and broadcasting an initial presence stanza. Arguments: event -- An empty dictionary. The session_start event does not provide any additional data. """ await self.get_roster() logging.info('Send presence') self.send_presence() self.join_room(self.room) def join_room(self, room): ''' Sends a presence stanza for the given chat room. Arguments: room -- The room to join. ''' self.plugin['xep_0045'].join_muc(room, self.nick) # , # If a room password is needed, use: # password=the_room_password, # wait=True) logging.info('Joined room {}'.format(room)) self.room_roster[room] = [] def muc_online(self, presence): """ Process a presence stanza from a chat room. In this case, we only add the sers nick to our room roster. Items in presence['muc'] are 'room', 'nick', 'jid', 'lang', 'role', 'affiliation'. Because 'jid' is (depends on server) possible empty, we only can add 'nick' to the roster. Arguments: presence -- The received presence stanza. See the documentation for the Presence stanza to see how else it may be used. """ nick = presence['muc']['nick'] room = presence['muc']['room'] if nick not in self.room_roster[room]: self.room_roster[room].append(nick) logging.debug('Roster: {}'.format(self.room_roster)) # if bot joins the room great greeting = ("Hello everybody, my name is {} and i'am the " "new kid in town. :)".format(self.nick)) if presence['muc']['nick'] == self.nick: self.send_message(mto = room, mbody = greeting, mtype = 'groupchat') def muc_offline(self, presence): """ Process a presence stanza from a chat room. At first we look for the nick who leaves the room. In case we are the user, we clear the roster and try to rejoin. Otherwise we remove the nick from roster. Arguments: presence -- The received presence stanza. See the documentation for the Presence stanza to see how else it may be used. """ nick = presence['muc']['nick'] room = presence['muc']['room'] if nick == self.nick: self.room_roster[room] = [] logging.info('Receive unavailable from {}'.format(room)) timeout = random.randint(0,10) logging.debug('Set timeout to {}'.format(timeout)) time.sleep(timeout) self.join_room(room) else: if nick in self.room_roster[room]: self.room_roster[room].remove(nick) logging.debug('Roster: {}'.format(self.room_roster)) def reconnect(self, event): ''' Deals with alls events for disconnections. Tryes to reconnect. ''' logging.warning('Receive a disconnect event: {}'.format(event)) logging.info('Try to reconnect') self.connect() def hangup(self): ''' Process a disconnect from server. Is only called from KeyboardInterrup exception to disconnect from server and terminate the process.. ''' self.disconnect() def run(self): ''' Registers needed plugins, connect the server and try to hold this connection. ''' self.register_plugin('xep_0045') # Multi-User Chat self.register_plugin('xep_0012') # Last Activity self.connect() try: self.process(forever=True) except KeyboardInterrupt: self.hangup()