Source code for shellbot.channel

# -*- coding: utf-8 -*-

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import logging
from six import string_types


[docs]class Channel(object): """ Represents a chat channel managed by a space A channel is a group of interactions within a chat space. It features a unique identifier, and a title. It also have participants and content. This class is a general abstraction of a communication channel, that can easily been adapted to various chat systems. It has been designed as a dictionary wrapper, with minimum exposure to shellbot, while enabling the transmission of rich information through serialization. Instances of Channel are created within a Space object, and consumed by it as well. For example, to create a channel with a given title, you could write:: channel = space.create(title='A new channel') bot.say(u"I am happy to join {}".format(channel.title)) And to change the title of the channel:: channel.title = 'An interesting place' space.update(channel) Direct channels support one-to-one interactions between the bot and one person. The creation of a direct channel can only be indirect, by sending an invitation to the target person. For example:: bot.say(person='foo.bar@acme.com', text='Do you want to deal with me?') If the person receives and accepts the invitation, the engine will receive a ``join`` event and load a new bot devoted to the direct channel. So, at the end of the day, when multiple persons interact with a shellbot, this involve both group and direct channels. For example, if you create a shellbot named shelly, that interacts with Alice and with Bob, then shelly will overlook multiple bots and channels: * shelly main channel (bot + channel + store + state machine) * direct channel with Alice (bot + channel + store + state machine) * direct channel with Bob (bot + channel + store + state machine) """ def __init__(self, attributes=None): """ Represents a channel generated by a chat space :param attributes: the set of atributes of this event :type attributes: dict or json-encoded string This function may raise AttributeError if some mandatory attribute is missing. """ if not attributes: self.__dict__['attributes'] = {} elif isinstance(attributes, string_types): self.__dict__['attributes'] = json.loads(attributes) else: self.__dict__['attributes'] = attributes def __getattr__(self, key): """ Provides access to any native attribute :param key: name of the attribute :type key: str :return: the value of the attribute This method is called when attempting to access a object attribute that hasn't been defined for the object. For example trying to access ``object.attribute1`` when ``attribute1`` hasn't been defined. ``Channel.__getattr__()`` checks original attributes to see if the attribute exists, and returns its value. Else an AttributeError is raised. """ try: return self.attributes[key] except KeyError: raise AttributeError(u"'{}' has no attribute '{}'".format( self.__class__.__name__, key)) def __setattr__(self, key, value): """ Changes an attribute :param key: name of the attribute :type key: str :param value: new value of the attribute :type value: str or other serializable object The use case for this function is when you adapt a channel that does not feature an attribute that is expected by shellbot. """ self.attributes[key] = value
[docs] def get(self, key, default=None): """ Returns the value of one attribute :param key: name of the attribute :type key: str :param default: default value of the attribute :type default: str or other serializable object :return: value of the attribute :rtype: str or other serializable object or None The use case for this function is when you adapt a channel that does not feature an attribute that is expected by shellbot. More specifically, call this function on optional attributes so as to avoid AttributeError """ value = self.attributes.get(key) # do not use default here! if value is None: value = default return value
def __repr__(self): """ Returns a string representing this object as valid Python expression. """ return u"{}({})".format( self.__class__.__name__, json.dumps(self.attributes, sort_keys=True)) def __str__(self): """ Returns a human-readable string representation of this object. """ return json.dumps(self.attributes, sort_keys=True) def __eq__(self, other): """ Compares with another object """ try: if self.attributes != other.attributes: return False return True except: return False # not same duck types @property def id(self): """ Returns channel unique id :rtype: str """ return self.__getattr__('id') @property def title(self): """ Returns channel title :rtype: str """ return self.__getattr__('title') @property def is_moderated(self): """ Indicates if this channel is moderated :rtype: str A channel is moderated when some participants have specific powers that others do not have. Else all participants are condidered the same and peer with each others. """ return self.attributes.get('is_moderated', False) @property def is_direct(self): """ Indicates if this channel is only for one person and the bot :rtype: str A channel is deemed direct when it is reserved to one-to-one interactions. Else it is considered a group channel, with potentially many participants. """ return self.attributes.get('is_direct', False)