Philipp Hörist pushed to branch master at gajim / python-nbxmpp
Commits: bcf7fd1f by Philipp Hörist at 2019-01-28T22:59:22Z Add UserMood (XEP-0107) module
- - - - - 1cbd94ea by Philipp Hörist at 2019-01-30T23:03:23Z Add UserActivity (XEP-0107) module
- - - - - a1f0d26a by Philipp Hörist at 2019-01-31T21:55:09Z Pubsub: Handle only new item notifications
- - - - -
6 changed files:
- nbxmpp/const.py - nbxmpp/dispatcher_nb.py - + nbxmpp/modules/activity.py - + nbxmpp/modules/mood.py - nbxmpp/modules/pubsub.py - nbxmpp/structs.py
Changes:
===================================== nbxmpp/const.py ===================================== @@ -283,3 +283,172 @@ class Error(Enum):
def __str__(self): return self.value + + +MOODS = [ + 'afraid', + 'amazed', + 'amorous', + 'angry', + 'annoyed', + 'anxious', + 'aroused', + 'ashamed', + 'bored', + 'brave', + 'calm', + 'cautious', + 'cold', + 'confident', + 'confused', + 'contemplative', + 'contented', + 'cranky', + 'crazy', + 'creative', + 'curious', + 'dejected', + 'depressed', + 'disappointed', + 'disgusted', + 'dismayed', + 'distracted', + 'embarrassed', + 'envious', + 'excited', + 'flirtatious', + 'frustrated', + 'grateful', + 'grieving', + 'grumpy', + 'guilty', + 'happy', + 'hopeful', + 'hot', + 'humbled', + 'humiliated', + 'hungry', + 'hurt', + 'impressed', + 'in_awe', + 'in_love', + 'indignant', + 'interested', + 'intoxicated', + 'invincible', + 'jealous', + 'lonely', + 'lost', + 'lucky', + 'mean', + 'moody', + 'nervous', + 'neutral', + 'offended', + 'outraged', + 'playful', + 'proud', + 'relaxed', + 'relieved', + 'remorseful', + 'restless', + 'sad', + 'sarcastic', + 'satisfied', + 'serious', + 'shocked', + 'shy', + 'sick', + 'sleepy', + 'spontaneous', + 'stressed', + 'strong', + 'surprised', + 'thankful', + 'thirsty', + 'tired', + 'undefined', + 'weak', + 'worried'] + + +ACTIVITIES = { + 'doing_chores': [ + 'buying_groceries', + 'cleaning', + 'cooking', + 'doing_maintenance', + 'doing_the_dishes', + 'doing_the_laundry', + 'gardening', + 'running_an_errand', + 'walking_the_dog'], + 'drinking': [ + 'having_a_beer', + 'having_coffee', + 'having_tea'], + 'eating': [ + 'having_a_snack', + 'having_breakfast', + 'having_dinner', + 'having_lunch'], + 'exercising': [ + 'cycling', + 'dancing', + 'hiking', + 'jogging', + 'playing_sports', + 'running', + 'skiing', + 'swimming', + 'working_out'], + 'grooming': [ + 'at_the_spa', + 'brushing_teeth', + 'getting_a_haircut', + 'shaving', + 'taking_a_bath', + 'taking_a_shower'], + 'having_appointment': [], + 'inactive': [ + 'day_off', + 'hanging_out', + 'hiding', + 'on_vacation', + 'praying', + 'scheduled_holiday', + 'sleeping', + 'thinking'], + 'relaxing': [ + 'fishing', + 'gaming', + 'going_out', + 'partying', + 'reading', + 'rehearsing', + 'shopping', + 'smoking', + 'socializing', + 'sunbathing', + 'watching_tv', + 'watching_a_movie'], + 'talking': [ + 'in_real_life', + 'on_the_phone', + 'on_video_phone'], + 'traveling': [ + 'commuting', + 'cycling', + 'driving', + 'in_a_car', + 'on_a_bus', + 'on_a_plane', + 'on_a_train', + 'on_a_trip', + 'walking'], + 'working': [ + 'coding', + 'in_a_meeting', + 'studying', + 'writing'] +} \ No newline at end of file
===================================== nbxmpp/dispatcher_nb.py ===================================== @@ -57,6 +57,8 @@ from nbxmpp.modules.captcha import Captcha from nbxmpp.modules.entity_caps import EntityCaps from nbxmpp.modules.blocking import Blocking from nbxmpp.modules.pubsub import PubSub +from nbxmpp.modules.activity import Activity +from nbxmpp.modules.mood import Mood from nbxmpp.modules.misc import unwrap_carbon from nbxmpp.modules.misc import unwrap_mam from nbxmpp.util import get_properties_struct @@ -181,6 +183,8 @@ class XMPPDispatcher(PlugIn): self._modules['EntityCaps'] = EntityCaps(self._owner) self._modules['Blocking'] = Blocking(self._owner) self._modules['PubSub'] = PubSub(self._owner) + self._modules['Mood'] = Mood(self._owner) + self._modules['Activity'] = Activity(self._owner)
for instance in self._modules.values(): for handler in instance.handlers:
===================================== nbxmpp/modules/activity.py ===================================== @@ -0,0 +1,87 @@ +# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com> +# +# This file is part of nbxmpp. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; If not, see http://www.gnu.org/licenses/. + +import logging + +from nbxmpp.protocol import NS_ACTIVITY +from nbxmpp.protocol import NS_PUBSUB_EVENT +from nbxmpp.protocol import Node +from nbxmpp.structs import StanzaHandler +from nbxmpp.structs import ActivityData +from nbxmpp.const import ACTIVITIES + +log = logging.getLogger('nbxmpp.m.activity') + + +class Activity: + def __init__(self, client): + self._client = client + self.handlers = [ + StanzaHandler(name='message', + callback=self._process_pubsub_activity, + ns=NS_PUBSUB_EVENT, + priority=16), + ] + + def _process_pubsub_activity(self, _con, stanza, properties): + if not properties.is_pubsub_event: + return + + if properties.pubsub_event.node != NS_ACTIVITY: + return + + item = properties.pubsub_event.item + + activity_node = item.getTag('activity', namespace=NS_ACTIVITY) + + activity, subactivity, text = None, None, None + for child in activity_node.getChildren(): + name = child.getName() + if name == 'text': + text = child.getData() + elif name in ACTIVITIES: + activity = name + subactivity = self._parse_sub_activity(child) + + if activity is None and activity_node.getPayload(): + log.warning('No valid activity value found') + log.warning(stanza) + return + + data = ActivityData(activity, subactivity, text) + log.info('Received activity: %s - %s', properties.jid, data) + properties.pubsub_event = properties.pubsub_event._replace(data=data) + + @staticmethod + def _parse_sub_activity(activity): + sub_activities = ACTIVITIES[activity.getName()] + for sub in activity.getChildren(): + if sub.getName() in sub_activities: + return sub.getName() + + def set_activity(self, data): + item = Node('activity', {'xmlns': NS_ACTIVITY}) + if data is not None and data.activity: + activity_node = item.addChild(data.activity) + if data.subactivity: + activity_node.addChild(data.subactivity) + if data.text: + item.addChild('text', payload=data.text) + + jid = self._client.get_bound_jid().getBare() + self._client.get_module('PubSub').publish( + jid, NS_ACTIVITY, item, id_='current')
===================================== nbxmpp/modules/mood.py ===================================== @@ -0,0 +1,78 @@ +# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com> +# +# This file is part of nbxmpp. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; If not, see http://www.gnu.org/licenses/. + +import logging + +from nbxmpp.protocol import NS_MOOD +from nbxmpp.protocol import NS_PUBSUB_EVENT +from nbxmpp.protocol import Node +from nbxmpp.structs import StanzaHandler +from nbxmpp.structs import MoodData +from nbxmpp.const import MOODS + +log = logging.getLogger('nbxmpp.m.mood') + + +class Mood: + def __init__(self, client): + self._client = client + self.handlers = [ + StanzaHandler(name='message', + callback=self._process_pubsub_mood, + ns=NS_PUBSUB_EVENT, + priority=16), + ] + + def _process_pubsub_mood(self, _con, stanza, properties): + if not properties.is_pubsub_event: + return + + if properties.pubsub_event.node != NS_MOOD: + return + + item = properties.pubsub_event.item + + mood_node = item.getTag('mood', namespace=NS_MOOD) + + mood, text = None, None + for child in mood_node.getChildren(): + name = child.getName().strip() + if name == 'text': + text = child.getData() + elif name in MOODS: + mood = name + + if mood is None and mood_node.getPayload(): + log.warning('No valid mood value found') + log.warning(stanza) + return + + data = MoodData(mood, text) + log.info('Received mood: %s - %s', properties.jid, data) + properties.pubsub_event = properties.pubsub_event._replace(data=data) + + def set_mood(self, data): + item = Node('mood', {'xmlns': NS_MOOD}) + if data is not None and data.mood: + item.addChild(data.mood) + + if data.text: + item.addChild('text', payload=data.text) + + jid = self._client.get_bound_jid().getBare() + self._client.get_module('PubSub').publish( + jid, NS_MOOD, item, id_='current')
===================================== nbxmpp/modules/pubsub.py ===================================== @@ -42,6 +42,7 @@ class PubSub: ]
def _process_pubsub_base(self, _con, stanza, properties): + properties.pubsub = True event = stanza.getTag('event', namespace=NS_PUBSUB_EVENT) items = event.getTag('items') if len(items.getChildren()) != 1: @@ -49,6 +50,8 @@ class PubSub: log.warning(stanza) node = items.getAttr('node') item = items.getTag('item') + if item is None: + return id_ = item.getAttr('id') properties.pubsub_event = PubSubEventData(node, id_, item, None)
===================================== nbxmpp/structs.py ===================================== @@ -60,6 +60,10 @@ StanzaIDData.__new__.__defaults__ = (None, None)
PubSubEventData = namedtuple('PubSubEventData', 'node id item data')
+MoodData = namedtuple('MoodData', 'mood text') + +ActivityData = namedtuple('ActivityData', 'activity subactivity text') +
class MAMData(namedtuple('MAMData', 'id query_id archive namespace timestamp')):
@@ -105,8 +109,13 @@ class MessageProperties: self.voice_request = None self.self_message = False self.mam = None + self.pubsub = False self.pubsub_event = None
+ @property + def is_pubsub(self): + return self.pubsub + @property def is_pubsub_event(self): return self.pubsub_event is not None
View it on GitLab: https://dev.gajim.org/gajim/python-nbxmpp/compare/18edd864e367a26b702965496c...