-
Notifications
You must be signed in to change notification settings - Fork 10
/
textchannel.py
166 lines (137 loc) · 5.99 KB
/
textchannel.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import dbus
import logging
import gi
gi.require_version('TelepathyGLib', '0.12')
from gi.repository import TelepathyGLib
CHANNEL_INTERFACE = TelepathyGLib.IFACE_CHANNEL
CHANNEL_INTERFACE_GROUP = TelepathyGLib.IFACE_CHANNEL_INTERFACE_GROUP
CHANNEL_TYPE_TEXT = TelepathyGLib.IFACE_CHANNEL_TYPE_TEXT
#CHANNEL_TYPE_FILE_TRANSFER = TelepathyGLib.IFACE_CHANNEL_TYPE_FILE_TRANSFER
CONN_INTERFACE_ALIASING = TelepathyGLib.IFACE_CONNECTION_INTERFACE_ALIASING
CONN_INTERFACE = TelepathyGLib.IFACE_CONNECTION
CHANNEL = TelepathyGLib.IFACE_CHANNEL
#CLIENT = TelepathyGLib.IFACE_CLIENT
CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES = \
TelepathyGLib.ChannelGroupFlags.CHANNEL_SPECIFIC_HANDLES
#CONNECTION_HANDLE_TYPE_CONTACT = TelepathyGLib.HandleType.CONTACT
CHANNEL_TEXT_MESSAGE_TYPE_NORMAL = TelepathyGLib.ChannelTextMessageType.NORMAL
#SOCKET_ADDRESS_TYPE_UNIX = TelepathyGLib.SocketAddressType.UNIX
#SOCKET_ACCESS_CONTROL_LOCALHOST = TelepathyGLib.SocketAccessControl.LOCALHOST
_logger = logging.getLogger('TextChannelWrapper')
from sugar3.presence import presenceservice
from sugar3.activity.activity import SCOPE_PRIVATE
from sugar3.graphics.alert import NotifyAlert
class TextChannelWrapper(object):
'''Wrapper for a telepathy Text Channel'''
def __init__(self, text_chan, conn):
'''Connect to the text channel'''
self._activity_cb = None
self._activity_close_cb = None
self._text_chan = text_chan
self._conn = conn
self._signal_matches = []
m = self._text_chan[CHANNEL_INTERFACE].connect_to_signal(
'Closed', self._closed_cb)
self._signal_matches.append(m)
def post(self, msg):
if msg is not None:
_logger.debug('post')
self._send(msg)
def _send(self, text):
'''Send text over the Telepathy text channel.'''
_logger.debug('sending %s' % text)
if self._text_chan is not None:
self._text_chan[CHANNEL_TYPE_TEXT].Send(
CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, text)
def close(self):
'''Close the text channel.'''
_logger.debug('Closing text channel')
try:
self._text_chan[CHANNEL_INTERFACE].Close()
except Exception:
_logger.debug('Channel disappeared!')
self._closed_cb()
def _closed_cb(self):
'''Clean up text channel.'''
for match in self._signal_matches:
match.remove()
self._signal_matches = []
self._text_chan = None
if self._activity_close_cb is not None:
self._activity_close_cb()
def set_received_callback(self, callback):
'''Connect the function callback to the signal.
callback -- callback function taking buddy and text args
'''
if self._text_chan is None:
return
self._activity_cb = callback
m = self._text_chan[CHANNEL_TYPE_TEXT].connect_to_signal(
'Received', self._received_cb)
self._signal_matches.append(m)
def handle_pending_messages(self):
'''Get pending messages and show them as received.'''
for identity, timestamp, sender, type_, flags, text in \
self._text_chan[
CHANNEL_TYPE_TEXT].ListPendingMessages(False):
self._received_cb(identity, timestamp, sender, type_, flags, text)
def _received_cb(self, identity, timestamp, sender, type_, flags, text):
'''Handle received text from the text channel.
Converts sender to a Buddy.
Calls self._activity_cb which is a callback to the activity.
'''
_logger.debug('received_cb %r %s' % (type_, text))
if type_ != 0:
# Exclude any auxiliary messages
return
if self._activity_cb:
try:
self._text_chan[CHANNEL_INTERFACE_GROUP]
except Exception:
# One to one XMPP chat
nick = self._conn[
CONN_INTERFACE_ALIASING].RequestAliases([sender])[0]
buddy = {'nick': nick, 'color': '#000000,#808080'}
_logger.debug('exception: received from sender %r buddy %r' %
(sender, buddy))
else:
# XXX: cache these
buddy = self._get_buddy(sender)
_logger.debug('Else: received from sender %r buddy %r' %
(sender, buddy))
self._activity_cb(buddy, text)
self._text_chan[
CHANNEL_TYPE_TEXT].AcknowledgePendingMessages([identity])
else:
_logger.debug('Throwing received message on the floor'
' since there is no callback connected. See'
' set_received_callback')
def set_closed_callback(self, callback):
'''Connect a callback for when the text channel is closed.
callback -- callback function taking no args
'''
_logger.debug('set closed callback')
self._activity_close_cb = callback
def _get_buddy(self, cs_handle):
'''Get a Buddy from a (possibly channel-specific) handle.'''
# XXX This will be made redundant once Presence Service
# provides buddy resolution
# Get the Presence Service
pservice = presenceservice.get_instance()
# Get the Telepathy Connection
tp_name, tp_path = pservice.get_preferred_connection()
obj = dbus.Bus().get_object(tp_name, tp_path)
conn = dbus.Interface(obj, CONN_INTERFACE)
group = self._text_chan[CHANNEL_INTERFACE_GROUP]
my_csh = group.GetSelfHandle()
if my_csh == cs_handle:
handle = conn.GetSelfHandle()
elif group.GetGroupFlags() & \
CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES:
handle = group.GetHandleOwners([cs_handle])[0]
else:
handle = cs_handle
# XXX: deal with failure to get the handle owner
assert handle != 0
return pservice.get_buddy_by_telepathy_handle(
tp_name, tp_path, handle)