File: jabberChat.py

package info (click to toggle)
pythoncard 0.8.2-2
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 8,452 kB
  • sloc: python: 56,787; makefile: 56; sh: 22
file content (323 lines) | stat: -rw-r--r-- 11,926 bytes parent folder | download | duplicates (3)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/usr/bin/python

"""
__version__ = "$Revision: 1.34 $"
__date__ = "$Date: 2005/12/13 11:13:23 $"
"""

from PythonCard import configuration, model, sound, timer
import threading 
import Queue
import ConfigParser
import wx

import os
import jabber
import time
import shutil

from chatWindow import ChatWindow
from groupChatWindow import GroupChatWindow
from connection import JabberConnection
import conferenceDialog

CONFIG_FILE = 'jabberChat.ini'    

class Chat(model.Background):

    def on_initialize(self, event):
        self.initSizers()
        self.loadConfig()
        self.displayOfflineUsers = 0
        self.roster = {}
        self.chatWindows = {}
        
        self.msgQueue = Queue.Queue()
        self.rosterQueue = Queue.Queue()

        self.jabberConnection = JabberConnection(self, self.account)
        self.thread = threading.Thread(target = self.jabberConnection.spinMyWheels)
        self.thread.setDaemon(1)
        self.thread.start()

        self.idleTimer = timer.Timer(self.components.listRoster, -1)
        self.idleTimer.start(1000)  # 1 second

        self.doResetIdle()

        # the Available and Do Not Disturb strings
        # should probably be settable as resource strings
        # the connection module references them as menu item labels
        # when the user starts the program, just default to Available
        self.statusBar.text = "Available"

    def initSizers(self):
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        sizer1.Add(self.components.listRoster, 1, wx.EXPAND)
        
        sizer1.Fit(self)
        sizer1.SetSizeHints(self)
        self.panel.SetSizer(sizer1)
        self.panel.SetAutoLayout(1)
        self.panel.Layout()

    def setChatWindowFont(self):
        for win in self.chatWindows.itervalues():
            win.setFonts(self.config['font'])

    def on_doSetFont_command(self, event):
        result = dialog.fontDialog(self, self.components.fldDocument.font)
        if result.accepted:
            self.config['font'] = result.font
            self.setChatWindowFont()

    def loadConfig(self):
        self.configPath = os.path.join(configuration.homedir, 'jabberchat')
        if not os.path.exists(self.configPath):
            os.mkdir(self.configPath)
        basePath = self.application.applicationDirectory
        configPath = os.path.join(self.configPath, CONFIG_FILE)
        if not os.path.exists(configPath):
            shutil.copy2(os.path.join(basePath, CONFIG_FILE), configPath)
        namesPath = os.path.join(self.configPath, 'names.txt')
        if not os.path.exists(namesPath):
            shutil.copy2(os.path.join(basePath, 'names.txt'), namesPath)
        parser = ConfigParser.ConfigParser()
        parser.read(configPath)
        self.account = {}
        self.account['server'] = parser.get('Account', 'server')
        self.account['username'] = parser.get('Account', 'username')
        self.account['password'] = parser.get('Account', 'password')
        self.account['resource'] = parser.get('Account', 'resource')

        self.config = {}
        # this needs to be made safe instead of using eval
        try:
            self.config['font'] = eval(parser.get('ChatWindow', 'font', None))
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            self.config['font'] = None
        try:
            self.config['playsound'] = eval(parser.get('Options', 'playsound'))
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            self.config['playsound'] = 0
        try:
            self.config['idletime'] = eval(parser.get('Options', 'idletime')) * 60
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            self.config['idletime'] = 0

        self.displayNames = {}
        try:
            f = open(namesPath)
            data = f.readlines()
            f.close()
            for line in data:
                jid, name = line.rstrip().split(',')
                self.displayNames[jid] = name
        except IOError:
            pass

    # when user selects "Available"
    # we need to reset
    def doResetIdle(self):
        self.checkForIdle = 1
        self.userIsIdle = 0
        self.startIdle = time.time()
        self.lastPosition = wx.GetMousePosition()
        
    def on_idle(self, event):
        # handle incoming Jabber messages
        if not self.msgQueue.empty():
            msg = self.msgQueue.get()
            self.doDisplayMsgReceived(msg)
            event.RequestMore()
        # handle roster changes
        if not self.rosterQueue.empty():
            jid, roster = self.rosterQueue.get()
            if jid is None:
                self.updateRosterDisplay(roster)
            else:
                self.updateGroupChatRosterDisplay(jid, roster) 
            event.RequestMore()

    # KEA 2002-11-17
    # updates to how the roster displays
    # I would like to move this into a MultiColumnList
    # with icons for online/offline...
    # 2002-11-30
    # moved from JabberConnection class
    # I'm still not sure that thread conflicts won't occur
    # but this at least seems safer
    # one thing that probably still needs to be added is a way
    # of always using the same display order
    def updateRosterDisplay(self, roster):
        items = []
        # use this instead of displayed list since the
        # display may have a name or jid and the roster
        # itself is a dictionary that doesn't match one to one
        # with self._parent.components.listRoster.items
        self.displayedRosterList = []
        # KEA 2003-06-04
        # roster dictionary might change in size
        # due to other thread, so make a list of keys
        rosterList = roster.keys()
        for key in rosterList:
            status = roster[key][0]
            show = roster[key][1]
            if show:
                show = " (%s)" % roster[key][1]
            else:
                show = ''
            name = self.displayNames.get(key, key)
            if status == 'online':
                items.append('* ' + name + show)
                self.displayedRosterList.append(key)
            elif self.displayOfflineUsers:
                items.append('  ' + name)
                self.displayedRosterList.append(key)
        self.components.listRoster.items = items
        self.roster = roster

    def updateGroupChatRosterDisplay(self, jid, roster):
        listRoster = self.chatWindows[jid].components.listRoster
        items = []
        for key in roster:
            status = roster[key][0]
            if status:
                items.append("%s (%s)" % (key, str(status)))
            else:
                items.append(key)
        items.sort()
        listRoster.items = items
        
    def on_listRoster_timer(self, event):
        # check for user idle if user is currently Available
        # but don't bother if Do Not Disturb... are set instead
        if self.checkForIdle and (self.config['idletime'] != 0):
            position = wx.GetMousePosition()
            if position == self.lastPosition:
                if self.userIsIdle == 0:
                    # check whether we've been idle too long
                    if (time.time() - self.startIdle) > self.config['idletime']:
                        self.userIsIdle = 1
                        self.jabberConnection.sendPresence("Idle")
                        self.statusBar.text = "Idle"
                        #print "***user is idle***"
            else:
                if self.userIsIdle:
                    #print "***user is no longer idle"
                    #print "there was no activity for", round((time.time() - self.startIdle) / 60), "minutes"
                    self.userIsIdle = 0
                    self.jabberConnection.sendPresence("Available")
                    self.statusBar.text = "Available"
                self.startIdle = time.time()
                self.lastPosition = position

    def createChatWindow(self, jid):
        # the jid should be in the form of username@domain
        win = model.childWindow(self, ChatWindow)
        win.setFonts(self.config['font'])
        # override resource position
        #win.SetPosition((425, -1))
        # just in case, convert to string, will this clear up Unicode issues?
        jid = str(jid)
        toName = self.displayNames.get(jid, jid)
        win.setToJID(jid, toName)
        win.visible = True
        self.chatWindows[jid] = win

    def createGroupChatWindow(self, jid, nickname=None):
        # the jid should be in the form of username@domain
        win = model.childWindow(self, GroupChatWindow)
        win.setFonts(self.config['font'])
        # override resource position
        #win.SetPosition((425, -1))
        # just in case, convert to string, will this clear up Unicode issues?
        jid = str(jid)
        toName = self.displayNames.get(jid, jid)

        if nickname is None:
            nickname = self.jabberConnection.username
        win.nickname = nickname

        win.setToJID(jid, toName)
        win.visible = True
        self.chatWindows[jid] = win
        self.jabberConnection.joinGroupChat(jid, nickname)

    def playIncomingSound(self):
        if self.config['playsound']:
            try:
                filename = os.path.join(self.application.applicationDirectory, 'incoming.wav')
                snd = sound.Sound(filename)
                snd.play(1, 0)
            except IOError:
                pass
        
    def doDisplayMsgReceived(self, data):
        if data is not None:
            jid, txt = data
            jid = str(jid)
            try:
                jid, resource = jid.split('/')
            except ValueError:
                resource = "default"
            if jid not in self.chatWindows:
                self.createChatWindow(jid)
            self.playIncomingSound()
            #self.components.fldTranscript.appendText(data + '\n')
            self.chatWindows[jid].appendMessage(jid + "/" + resource, txt)
        else:
            pass

    # this code is dependent on the format
    # of the text in the list
    # so a change to the updateRosterDisplay
    # method in the connection module must be
    # reflected here until the jids are stored
    # outside the list
    def on_listRoster_mouseDoubleClick(self, event):
        jid = self.displayedRosterList[event.target.selection]
        if jid not in self.chatWindows:
            self.createChatWindow(jid)
        else:
            self.chatWindows[jid].visible = True
        # make sure the chat window is in front
        # and isn't minimized (iconized)
        if self.chatWindows[jid].IsIconized():
            self.chatWindows[jid].Iconize(0)
        wx.CallAfter(self.chatWindows[jid].Raise)

    def on_close(self, event):
        self.jabberConnection.keepRunning = 0
        self.jabberConnection.disconnect()
        self.idleTimer.stop()
        event.skip()
        
    def on_changeStatus_command(self, event):
        self.jabberConnection.sendPresence(event.target.label)
        self.statusBar.text = event.target.label
        if event.target.label == "Available":
            self.doResetIdle()
        else:
            self.checkForIdle = 0

    def on_menuOptionsDisplayOfflineUsers_select(self, event):
        self.displayOfflineUsers = event.IsChecked()
        self.updateRosterDisplay(self.roster)

    def on_menuFileJoinConference_select(self, event):
        room = ''
        server = None
        nickname = self.jabberConnection.username
        result = conferenceDialog.conferenceDialog(self, '', server, nickname)
        if result.accepted:
            room = result.room
            server = result.server
            nickname = result.nickname
            jid = room + '@' + server
            self.createGroupChatWindow(jid, nickname)


if __name__ == '__main__':
    app = model.Application(Chat)
    app.MainLoop()