#!/usr/bin/env python
#
# $Id: wcc_client.py,v 1.3 2001/11/03 11:05:22 doughellmann Exp $
#
# Copyright 2001 Doug Hellmann.
#
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""GUI Client to talk to a WackyChat server.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: wcc_client.py,v $',
'rcs_id' : '$Id: wcc_client.py,v 1.3 2001/11/03 11:05:22 doughellmann Exp $',
'creator' : 'Doug Hellmann <doug@hellfly.net>',
'project' : 'PmwContribD',
'created' : 'Sun, 27-May-2001 11:42:08 EDT',
#
# Current Information
#
'author' : '$Author: doughellmann $',
'version' : '$Revision: 1.3 $',
'date' : '$Date: 2001/11/03 11:05:22 $',
}
#
# Import system modules
#
import string
import socket
import sys
import os
import Pmw
from Tkinter import *
#
# Import Local modules
#
import GuiAppD
from MCScrolledListBox import MCScrolledListBox
import Table
import wcc_dialog
from wcc_options import hermesrc
import wcc_protocol
from wcc_status import WackyStatusInfo
import cvsversion
#
# Module
#
class ChangePasswordDialog(Pmw.Dialog):
OK = 'OK'
CANCEL = 'Cancel'
def __init__(self, parent=None, **kw):
optiondefs = (
('currentpassword', '', None),
('setpassword', None, Pmw.INITOPT),
('buttons', (self.OK, self.CANCEL), self._buttons),
('defaultbutton', 0, self._defaultButton),
('command', self.commandCB, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
Pmw.Dialog.__init__(self, parent)
#
# Create an entry field for the old password
#
self.old_password = StringVar(self.interior())
self.old_password.set('')
old = self.createcomponent(
'old',
(), None,
Pmw.EntryField,
(self.interior(),),
labelpos='w',
label_text='Current Password:',
entry_textvariable=self.old_password,
entry_show='*',
)
old.pack(side=TOP)
self.new_password = StringVar(self.interior())
new = self.createcomponent(
'new',
(), None,
Pmw.EntryField,
(self.interior(),),
labelpos='w',
label_text='New Password',
entry_textvariable=self.new_password,
entry_show='*',
)
new.pack(side=TOP)
Pmw.alignlabels( (new, old) )
self.initialiseoptions(self.__class__)
return
def activate(self):
self.old_password.set('')
self.new_password.set('')
self.component('old_entry').focus_set()
return Pmw.Dialog.activate(self)
def errorMessage(self, message):
dlg = Pmw.MessageDialog(self.interior(),
title='Invalid Password',
message_text=message,
iconpos='w',
icon_bitmap='error',
defaultbutton=0)
dlg.activate()
return
def commandCB(self, button):
if button == self.CANCEL:
self.deactivate()
return
elif button == self.OK:
#
# Test old password
#
old_val = string.strip(self.old_password.get())
if not old_val:
# They did not enter a password at all
self.errorMessage('You must enter a value for the current password.')
return
elif old_val != self['currentpassword']:
# They did not get the current value right!
self.errorMessage('Incorrect value for the current password.')
return
else:
# See about changing the password
new_val = string.strip(self.new_password.get())
if not new_val:
self.errorMessage('No value specified for new password.')
return
elif ((string.find(new_val, ' ') >= 0)
or
(string.find(new_val, '\t') >= 0)):
# No whitespace allowed
self.errorMessage(
'Whitespace characters are not allowed in passwords.')
return
else:
# Seems ok, set the new value
setpassword = self['setpassword']
if setpassword:
setpassword(new_val)
self.deactivate()
return
class Hermes(GuiAppD.GuiAppD):
#
# About box information
#
appversion=cvsversion.cvs_product_version()
appname='Hermes'
copyright = '\nClient program:\nCopyright Doug Hellmann\n' +\
'<doug@hellfly.net> 1999\n' + \
'All rights reserved.\n\n' + \
'Webguys Instant Message (WIM)\nServer protocol:\n' + \
'(AKA Wacky-Chat)\n' + \
'Copyright 1998 Kevin Birch <kbirch@pobox.com>'
usebuttonbox = 1
#
# Broadcast dialog title
#
BROADCAST_DIALOG_TITLE = 'Broadcast Messages'
#
# How frequently to poll?
#
polling_frequency = 100
user_preferences = hermesrc()
ONLINE = 'Online'
AWAY = 'Away'
NA = 'Not Available'
OCCUPIED = 'Occupied'
DND = 'Do not disturb'
PRIVACY = 'Privacy'
AUTO_RESPONSE = 'Auto-response'
dispositions = (ONLINE,
AWAY,
NA,
OCCUPIED,
DND,
PRIVACY,
)
disposition_message = {
ONLINE : None,
AWAY : 'araway',
NA : 'arna',
OCCUPIED : 'aroccupied',
DND : 'ardnd',
PRIVACY : 'arprivacy',
}
disposition_protocol_param = {
ONLINE : 'ONLINE',
AWAY : 'AWAY',
NA : 'NA',
OCCUPIED : 'OCCUPIED',
DND : 'DND',
PRIVACY : 'PRIVACY',
}
disposition_display_value = {
'ONLINE' : ONLINE,
'AWAY' : AWAY,
'NA' : NA,
'OCCUPIED' : OCCUPIED,
'DND' : DND,
'PRIVACY' : PRIVACY,
}
shown_initial_motd = 0
##
## GUIAPP STUFF
##
def appInit(self):
#
# Interface not created yet
#
self._interface_created = 0
#
# User preferences
#
self.user_preferences.store() # only really useful the first time
#
# My current online status
#
self.disposition = self.disposition_protocol_param[self.ONLINE]
#
# Find the MOTD that we know already
#
self._motdRead()
#
# Storage for Messages of the day
#
self._motd = []
#
# Storage for message dialogs, so we can
# reuse them
#
self._message_dialogs = {}
return
def main(self):
if self._wimd:
GuiAppD.GuiAppD.main(self)
return
##
## MAKE THE INTERFACE
##
def createMenuBar(self):
#
# File menu
#
self.addMenuItem('File', 'command', 'Connect to the server',
label='Connect',
command=self.connect
)
self.addHelpItem('Interface:Menu:File:Connect',
"""
Connect to the WIMD server.
"""
)
self.addMenuItem('File', 'command', 'Disconnect from the server',
label='Disconnect',
command=self.disconnect
)
self.addHelpItem('Interface:Menu:File:Disconnect',
"""
Disconnect from the WIMD server.
"""
)
self.menuBar.addmenuitem('File', 'separator')
self.addMenuItem('File', 'command', 'Send a broadcast message',
label='Broadcast...',
command=self.broadcastMessage
)
self.addHelpItem('Interface:Menu:File:Broadcast...',
"""
Send a broadcast message to all current
users.
""")
self.menuBar.addmenuitem('File', 'separator')
#
# Options
#
self.addMenuItem('Options', 'command', 'Change password',
label='Password...',
command=self.showChangePasswordCB,
)
self.addHelpItem('Interface:Menu:Options:Password...',
"""
Change the users password on the server.
"""
)
#
# Info menu
#
try:
self.menuBar.addmenu('Info', 'Server and user information')
except ValueError:
pass
self.addMenuItem('Info', 'command', 'Server daily messages',
label='MOTD...',
command=self.showMOTD
)
self.addHelpItem('Interface:Menu:Info:MOTD...',
"""
The MOTD menu item displays a dialog containing
all of the message of the day information from the
last time a connection to the server was initiated.
""")
self.addMenuItem('Info', 'command', 'Update user status list',
label='Update users',
command=self.statusUpdateCB,
)
self.addHelpItem('Interface:Menu:Info:Update users',
"""
Poll the server to get the status of all
current users. This happens periodically,
but it can be useful to scan manually.
""")
self.addMenuItem('Info', 'command',
'Show all information about the selected user',
label='Show user info...',
command=self.showUserInfoCB,
acceleratorKey='i')
self.addHelpItem('Interface:Menu:Info:Show user info...',
"""
Display all of the information about
the selected user.
""")
GuiAppD.GuiAppD.createMenuBar(self)
return
def createInterface(self):
#
# Double check that we know enough to
# sign them on. This cannot be done
# in appInit() because we do not have
# all of the required GUI pieces initialized
# yet.
#
self.connect()
if self._wimd:
self.configureWimd()
self.createChatInterface()
return
def createChatInterface(self):
#
# Create the actual interface
#
self.component('progressmeter').pack_forget()
self.addHelpItem('Interface:Main Window',
"""
The main window displays the list of users
who are currently available for chats, and
provides menu options and controls for configuring
the behavior of the application.
""")
#
# Create a table to hold the user information
# for all the users on the system
#
user_list = self.createcomponent(
'usertable',
(), None,
MCScrolledListBox,
(self.interior(),),
dblclickcommand=self.newChatWithSelected,
clipper_width=285,
clipper_height=200,
)
for cl, ex, w in ( ('User', YES, 20),
('Status', NO, 10),
('M', NO, 5),
):
user_list.addColumn(label=cl, expand=ex, width=w)
self.bind(user_list, 'Double click on a user to send them a message')
self.addHelpItem('Interface:Main Window:User List',
"""
The user list displays a list of the users
currently connected to the wimd server. The
list is automatically updated every time
a user logs on or off from the server.
Double click on a user name to open a
chat window with that user.
""")
self._interface_created = 1
self.wStatusUpdate()
user_list.pack(
side=TOP,
fill=BOTH,
expand=YES,
)
disposition = self.createcomponent(
'DispositionMenu',
(), None,
Pmw.OptionMenu,
(self.interior(),),
items=self.dispositions,
initialitem=0,
labelpos='w',
label_text='Status:',
command=self.changeDispositionCB,
)
self.bind(disposition, 'Set your online status')
disposition.pack(
side=TOP,
fill=X,
expand=NO,
)
#
# Create buttons
#
self.addHelpItem('Interface:Main Window:Action Buttons',
"""
The action buttons along the bottom of the
main window provide quick access to commonly
executed chat commands.
""")
self.buttonAdd('Chat',
helpMessage='Chat with selected users',
statusMessage='Chat with selected users',
command=self.newChatWithSelected,
)
self.addHelpItem('Interface:Main Window:Action Buttons:Chat',
"""
Use this button to create a chat window to talk
to the user(s) selected in the User List.
""")
self.buttonAdd('Broadcast',
helpMessage='Send a broadcast message to all users',
statusMessage='Send a broadcast message to all users',
command=self.broadcastMessage,
)
self.addHelpItem(
'Interface:Main Window:Action Buttons:Broadcast',
"""
Use this button to send a message to all users who
are currently logged in to the server.
""")
self.buttonAdd('Stored',
helpMessage='Retrieve stored messages',
statusMessage='Retrieve stored messages',
state='disabled',
)
self.addHelpItem(
'Interface:Main Window:Action Buttons:Stored',
"""
Use this button to display messages which have
been received and stored by the client because the
status was set to 'Do not disturb'.
"""
)
#
# Register the polling function
#
self.after(self.polling_frequency, self.poll)
return
##
## CALLBACKS
##
def changePasswordCB(self, new_value):
print 'changing password: %s' % new_value
try:
self._wimd.passwd(new_value)
except wcc_protocol.WackyProtocolError, msg:
self.showError(str(msg))
return
except IOError:
pass
self.user_preferences['password'] = new_value
return
def showChangePasswordCB(self, *args):
try:
dlg = self._change_password_dialog
except AttributeError:
dlg = ChangePasswordDialog(self.interior(),
setpassword=self.changePasswordCB)
self._change_password_dialog = dlg
dlg.configure(currentpassword=self.user_preferences['password'])
dlg.activate()
return
def statusUpdateCB(self, *args):
self.busyStart()
if self._connected:
self._status_info.update()
self.wStatusUpdate()
self.busyEnd()
return
def showUserInfoCB(self, event=None):
selidxs = self.component('usertable').curselection()
if not selidxs:
return
selected_list = []
for idx in selidxs:
idx = int(idx)
selected_list.append(self._display_user_info[idx])
selected_list.sort()
info = []
for sel in selected_list:
info.append('User:\t%s\nUID:\t%s\nStatus:\t%s\nEmail:\t%s' % sel)
info = string.join(info, '\n\n')
btn = self.showMessageDialog(info, justify='left',
buttons=('Chat', 'OK'),
title='User Information')
if btn == 'Chat':
self.newChatWithSelected()
return
def broadcastMessage(self):
dlg = self.getDialog( self.BROADCAST_DIALOG_TITLE, (), () )
dlg.deiconify()
dlg.show()
return
def savePrefsCB(self, prefs):
"""
We need to update the preferences for dialogs
that can be changed at runtime.
"""
#print 'Saved preferences: ', prefs
new_dialog_settings = {
'ringbell':prefs['ringbell'],
'dateface':prefs['dateface'],
'fromface':prefs['fromface'],
'messageface':prefs['messageface'],
'urlface':prefs['urlface'],
'clearonclose':prefs['clearwindow'],
'webbrowser':prefs['webbrowser'],
}
apply(wcc_dialog.updateDialogSettings, (), new_dialog_settings)
GuiAppD.GuiAppD.savePrefsCB(self, prefs)
try:
self._wimd.nick(prefs['nickname'])
if prefs['email']:
self._wimd.mail(prefs['email'])
except AttributeError:
# no connection yet
pass
return
def changeDispositionCB(self, *args):
self.disposition = self.disposition_protocol_param[args[0]]
self._wimd.disp(self.disposition)
self._status_info.setUserStatus(str(self.user_preferences['uid']),
self.disposition)
self.wStatusUpdate()
return
##
## WIMD MESSAGE HANDLERS
##
def poll(self):
"""
Poll the wimd connection.
"""
if self._connected:
messages = self._wimd.poll()
#if messages:
# print 'Got messages: ', messages
self.after(self.polling_frequency, self.poll)
return
def _wimdmhMESG(self, fromType, fromName, fromID, message):
self.busyStart()
#
# Determine how the message was addressed
#
if message[:3] == 'To:':
eofln = string.index(message, '\r')
addressee = message[3:eofln]
message = message[eofln+2:]
else:
addressee = None
#
# Determine if we have a message window from this
# user already
#
if addressee:
# Use the addressee list to find the window
addressee_list = string.split(addressee, ',')
addressee_list = map(string.strip, addressee_list)
addressee_list.sort()
dlg_id = self.getDialogId(addressee_list + [fromName])
addressee_id_list = map(self._status_info.uidFromName,
addressee_list)
pass
else:
# Use the from id to find the window
dlg_id = self.getDialogId( (fromName,) )
addressee_list = (fromName,)
addressee_id_list = (fromID,)
pass
#
# Get the dialog, add the message, make the dialog
# visible.
#
dlg = self.getDialog( dlg_id, addressee_list, addressee_id_list )
dlg.addMessage(fromName, message)
disp_key = self.disposition_display_value[self.disposition]
if ((disp_key != 'ONLINE')
and self.user_preferences['senddisposition']):
try:
pref_name = self.disposition_message[disp_key]
if pref_name:
auto_response = '%s\n\n%s' \
% (self.user_preferences['arprefix'],
self.user_preferences[pref_name])
else:
auto_response = None
except KeyError:
auto_response = None
if auto_response:
dlg.sendMessage(auto_response)
self.busyEnd()
return
def _wimdmhCAST(self, fromType, fromName, fromID, message):
if fromID != str(self.user_preferences['uid']):
dlg = self.getDialog( self.BROADCAST_DIALOG_TITLE, (), () )
dlg.addMessage(fromName, message)
return
def _wimdmhMOTD(self, message):
#print 'MOTD:', message
self._motd.append(message)
return
def _wimdmhUPDT(self, *args):
#print '_wimdmhUPDT', args
if args[0] in ( 'USER', 'DISP' ):
# User signed on or off
if args[2] == str(self.user_preferences['uid']):
# this is us, and we know our state
return
elif args[0] == 'NICK':
pass
elif args[0] == 'SERV':
# Server status message
if args[1] == 'KICK':
msg = 'You have been kicked off of the server.'
elif args[1] == 'DOWN':
msg = 'The server is going down.'
elif args[1] == 'DISCONNECT':
msg = 'You have been disconnected from the server.'
else:
msg = 'Unknown server message: %s' % str(args)
self.showMessageDialog(msg)
self.disconnect(1)
#
# Retrieve status info and redisplay
#
self.after_idle(self.statusUpdateCB)
return
def configureWimd(self):
#
# Set up the various "callbacks" for messages coming
# in over the wimd connection
#
for msg_type in (
'MOTD',
'MESG',
'UPDT',
'CAST',
):
self._wimd.registerMessageHandler(
msg_type, getattr(self, '_wimdmh%s' % msg_type))
return
##
## SUPPORT ROUTINES
##
def _motdRead(self):
"""
Read from the motd file that we stored last time.
"""
try:
motdFile = open(os.path.join(os.environ['HOME'], '.hermes.motd'),
'rt')
except KeyError:
# No home directory set
self.previous_motd = ''
except IOError:
# No file written yet
self.previous_motd = ''
else:
self.previous_motd = motdFile.read()
motdFile.close()
return
def _motdWrite(self, new_motd_str):
print '_motdWrite'
try:
motdFileName = os.path.join(os.environ['HOME'], '.hermes.motd',)
motdFile = open(motdFileName, 'wt')
except KeyError:
# No home directory set
return
except IOError:
# cannot write to file
return
else:
print '\twriting to %s' % motdFileName
motdFile.write(new_motd_str)
motdFile.close()
return
def showNewMotd(self):
if self.shown_initial_motd:
return
new_motd_str = string.join(self._motd, '\n')
if not new_motd_str:
return
self.shown_initial_motd = 1
if new_motd_str == self.previous_motd:
return
self.after_idle(self.showMOTD)
self._motdWrite(new_motd_str)
return
def showMOTD(self):
"""
Show a dialog with the message of the day.
"""
motd = '%s\n\nContact Doug Hellmann <doug@hellfly.net>\nfor problems with Hermes.' \
% string.join(self._motd, '\n')
self.showMessageDialog(motd, title='Server Message of the Day')
return
def getDialogId(self, users):
whoami = self._status_info.nameFromUid(self.user_preferences['uid'])
users = filter(lambda x, ignore=whoami: x != ignore, users)
if not users:
users = [ whoami ]
return wcc_dialog.getMessageDialogIDFromUsers(users)
def wStatusUpdate(self):
"""
Update the user information presented in the interface.
"""
if not self._interface_created: return
#print 'status info: ', self._status_info
#
# Reduce to only online users:
#
#online_users = filter( lambda x: x[2] == 'ONLINE',
# self._status_info)
if self._connected:
if self.user_preferences['ignoreself']:
display_info = filter(
lambda x,
ignore_id=str(self.user_preferences['uid']):
x[1] != ignore_id,
self._status_info)
else:
display_info = self._status_info
#print 'display_info : ', display_info
self._display_user_info = display_info
usernames = map( lambda x: str(x[0]), display_info)
#user_status = map( lambda x: str(x[2]), display_info)
user_status = map(lambda x, m=self.disposition_display_value: \
str(m[x[2]]),
display_info)
else:
usernames = []
user_status = []
self.component('usertable').setlists( (usernames, user_status) )
return
def newChatWithSelected(self, event=None):
"""
Create a new chat session (window) with the
currently selected names from the usertable.
"""
selidxs = self.component('usertable').curselection()
if not selidxs:
return
selected_list = []
for idx in selidxs:
idx = int(idx)
selected_list.append(self._display_user_info[idx])
selected_list.sort()
addressee_list = map(lambda x: x[0], selected_list)
addressee_id_list = map(lambda x: x[1], selected_list)
#print 'address message to ', addressee_list
self.sendMessage(addressee_list, addressee_id_list)
return
def getDialog(self, dlg_id, addressee_list, addressee_id_list):
dlg = wcc_dialog.getMessageDialog(
parent=self.interior(),
uid=self.user_preferences['uid'],
uname=self._status_info.nameFromUid(self.user_preferences['uid']),
dialogID=dlg_id,
addressees=addressee_list,
addressee_ids=addressee_id_list,
app=self,
webbrowser=self.user_preferences['webbrowser'],
ringbell=self.user_preferences['ringbell'],
dateface=self.user_preferences['dateface'],
fromface=self.user_preferences['fromface'],
messageface=self.user_preferences['messageface'],
urlface=self.user_preferences['urlface'],
clearonclose=self.user_preferences['clearwindow'],
)
return dlg
def sendMessage(self, addressee_list=(), addressee_id_list=()):
"""
Send a message to a list of users.
"""
dlg_id = self.getDialogId(addressee_list)
dlg = self.getDialog(dlg_id, addressee_list, addressee_id_list)
dlg.show()
return
##
## Connection stuff
##
def disconnect(self, forced=0):
#if not forced:
self._wimd.quit()
del self._wimd
self._wimd = None
self._connected = None
self.wStatusUpdate()
self.showMessage('busy', 'Disconnected')
return
def connect(self):
self._motd = []
num_tries = 2
self._connected = 0
while num_tries > 0:
try:
self.login()
except wcc_protocol.WackyProtocolError, msg:
self._wimd = None
self.showError('Login error:\n\n%s\n\nNot connected.' % msg[0])
self.showPrefsDialog()
num_tries = num_tries - 1
else:
num_tries = 0
try:
self._status_info = WackyStatusInfo(self._wimd)
except IOError:
# broken pipe, probably bad login
self.showError(
'Problem connecting to server.\nProbably a bad password?')
self.showChangePasswordCB()
self.quit()
else:
self._connected = 1
self.showMessage('busy', None)
self.wStatusUpdate()
#
# At this point, we will have received
# other messages besides the unknown number of MOTD
# messages. That means we can display the new
# MOTD, if it is different from the previous one.
#
self.showNewMotd()
#
# Register the polling function
#
self.after(self.polling_frequency, self.poll)
return
def login(self, first_time=1):
if ((self.user_preferences['password'] == '')
or
(self.user_preferences['uid']) == 0):
#
# Need to prompt them for their password
#
self.getLoginInfo()
#
# Make the connection
#
try:
self._wimd = wcc_protocol.WackyConnection(
hostname=self.user_preferences['hostname'],
port=self.user_preferences['port'],
userid=str(self.user_preferences['uid']),
password=self.user_preferences['password'],
)
except socket.error, errMsg:
#
# Socket connection problem.
#
msg = 'An error occured connecting to the server\n' + \
('on port %d of %s as the user %s.\n' \
% (self.user_preferences['port'],
self.user_preferences['hostname'],
self.user_preferences['uid'])
) + \
'Verify the settings in your preferences file' + \
'(~/.hermesrc).'
if errMsg[0] == 111:
msg = msg + \
'\n\nCheck to make sure that the server is running.'
else:
msg = msg + '\n\nThe message was:\n%s' % str(errMsg)
self.showError(msg)
self._connected = 0
self._wimd = None
else:
self._connected = 1
self.configureWimd()
return
def getLoginInfo(self):
self.showMessageDialog(
'No connection information has been specified.\n'
'Set the user id, password, server, and port preferences\n'
'to continue.'
)
self.showPrefsDialog()
return
##
## Service methods
##
def pick_user(self):
"""
pick_user(parent, wimd)
Given a parent widget and a connection to a wimd server, present a
dialog to get the user to pick multiple users.
"""
status = self._status_info
dialog = Pmw.SelectionDialog( parent=self.interior() )
dialog.component('buttonbox').setdefault(0)
addable_users = map(lambda x: x[0], status)
dialog.component('scrolledlist').setlist(addable_users)
result = dialog.activate()
if result == 'OK':
# get selection
moniker = dialog.getcurselection()[0]
uid = status.uidFromName(moniker)
else:
moniker = None
uid = None
return moniker, uid
def connection(self):
"""
Get a reference to the wimd connection for
this application.
"""
return self._wimd
def statusInfo(self):
"""
Get a reference to the wimd status manager
for this application.
"""
return self._status_info
if __name__ == '__main__':
Hermes().run()
|