home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 5 / Amiga Tools 5.iso / tools / developer-tools / andere sprachen / python-1.3 / demo / chatserver.py < prev    next >
Encoding:
Python Source  |  1996-07-16  |  9.0 KB  |  311 lines

  1. #
  2. #    Multiuser Chat Server. (6-apr-96) by Irmen de Jong.
  3. #
  4. #    Implements a mud-like "chat" server to which multiple users can connect,
  5. #    by simple telnet. Some simple commands are supported.
  6. #    Feelings are supported, but not fully. No adverbs yet.
  7. #
  8. #    Usage: python chatserv.py [hostname] [portnumber]
  9. #
  10.  
  11. import select
  12. import string
  13. import sys
  14. import time
  15. import socket
  16.  
  17. WelcomeMsg = "\r\nTELNET CHAT SERVICE...\r\n"
  18.  
  19. class connection:
  20.     def __init__(self,sock,address,name=None):
  21.         self.sock=sock
  22.         self.name=name
  23.         self.address=address
  24.  
  25.     def __del__(self):
  26.         self.sock.close()
  27.         self.sock=None
  28.  
  29.     def send(self,msg):
  30.         self.sock.send(msg)
  31.     def senderr(self,errmsg):
  32.         self.sock.send(mkerr(errmsg))
  33.     def sendnote(self,msg):
  34.         self.sock.send(mknote(msg))
  35.  
  36.  
  37. # The following list contain all open connections.
  38. connections=[]
  39.  
  40. #Feeling types:
  41. SIMP=0; DEFA=1; PERS=2; QUAD=3; PREV=4; PHYS=5; DEUX=6; SHRT=7
  42.  
  43. #feelings.. taken from LPC mud soul. Not yet fully supported.
  44. feelings = {
  45. "hmm":        [0,0," hmm$ \nHOW \nAT"," at"],
  46. "burp":        [1,["rudely"],""," at"],
  47. "wink":        [1,["suggestively"],""," at"],
  48. "smile":    [1,["happily"],""," at"],
  49. "point":    [1,0,""," at"],
  50. "grin":        [1,["evilly"],""," at"],
  51. "laugh":    [1,0,""," at"],
  52. "nod":        [1,["solemnly"],""," at"],
  53. "wave":        [1,["happily"],""," at"],
  54. "cackle":    [1,["with glee"],""," at"],
  55. "chuckle":    [1,["politely"],""," at"],
  56. "bow":        [1,0,""," to"],
  57. "glare":    [1,["stonily"],""," at"],
  58. "giggle":    [1,["merrily"],""," at"],
  59. "groan":    [1,0,""," at"],
  60. "grunt":    [1,0,""," at"],
  61. "growl":    [1,0,""," at"],
  62. "snarl":    [1,0,""," at"],
  63. "recoil":    [1,["with fear"],""," from"],
  64. "moan":        [1,0,""," at"],
  65. "howl":        [1,["in pain"],""," at"],
  66. "puke":        [1,0,""," on"],
  67. "sneeze":    [1,["loudly"],""," at"],
  68. "spit":        [1,0,""," on"],
  69. "stare":    [1,0,""," at"],
  70. "whistle":    [1,["appreciatively"],""," at"],
  71. "applaud":    [1,0,""," at"],
  72. "agree":    [1,0,""," with"],
  73. "disagree":    [1,0,""," with"],
  74. "fart":        [1,0,""," at"],
  75. "dance":    [1,0,""," with"],
  76. "purr":        [1,0,""," at"],
  77. "listen":    [1,0,""," to"],
  78. "apologize":    [1,0,""," to"],
  79. "complain":    [1,0,""," about"],
  80. "beg":        [2,0," beg$ \nHOW"," beg$ \nWHO for mercy \nHOW"],
  81. "shake":    [2,0," shake$ \nYOUR head \nHOW"," shake$ hands with \nWHO \nHOW"],
  82. "grimace":    [0,0," \nHOW make$ an awful face \nAT"," at"],
  83. "stomp":    [2,0," stomp$ \nYOUR foot \nHOW"," stomp$ on \nPOSS foot \nHOW"],
  84. "snigger":    [1,["jeeringly"],""," at"],
  85. "watch":    [3,["carefully"]," watch the surroundings \nHOW", " watches the surroundings \nHOW"," watch \nWHO \nHOW"," watches \nWHO \nHOW",],
  86. "scratch":    [3,["on the head"]," scratch \nMYself \nHOW"," scratches \nMYself \nHOW"," scratch \nWHO \nHOW"," scratches \nWHO \nHOW",],
  87. "tap":        [2,["impatiently",0,"on the shoulder"]," tap$ \nYOUR foot \nHOW"," tap$ \nWHO \nWHERE"],
  88. "curse":    [2,0," curse$ \nWHAT \nHOW"," curse$ \nWHO \nHOW"],
  89. "sing":        [0,0," \nHOW sing$ \nWHAT \nAT"," to"],
  90. "mumble":    [0,0," mumble$ \nWHAT \nHOW \nAT"," to"],
  91. "scream":    [0,["loudly"]," scream$ \nWHAT \nAT \nHOW"," at"],
  92. "yell":        [0,["in a high pitched voice"]," yell$ \nHOW \nWHAT \nAT"," at"],
  93. "hide":        [0,0," hide$ \nHOW behind \nWHO"],
  94. "pounce":    [5,["playfully"],""],
  95. "kneel":    [0,0," \nHOW fall$ on \nYOUR knees \nAT"," in front of"],
  96. "roll":        [0,["to the ceiling"]," roll$ \nYOUR eyes \nHOW"],
  97. "boggle":    [0,0," boggle$ \nHOW at the concept"],
  98. "cheer":    [7,["enthusiastically"],""],
  99. "wiggle":    [0,0," wiggle$ \nYOUR bottom \nHOW"],
  100. "flip":        [0,0," flip$ \nHOW head over heels"],
  101. "cry":        [6,0," cry \nHOW"," cries \nHOW"],
  102. "sob":        [7,0,""],
  103. "sweat":    [7,0,""],
  104. "gurgle":    [7,0,""],
  105. "grumble":    [7,0,""],
  106. "die":        [6,0," fall \nHOW down and play dead"," falls \nHOW to the ground, dead"],
  107. "stumble":    [7,0,""],
  108. "bounce":    [7,0,""],
  109. "yawn":        [7,0,""],
  110. "sulk":        [7,["in the corner"],""],
  111. "strut":    [7,["proudly"],""],
  112. "sniff":    [7,0,""],
  113. "snore":    [7,0,""],
  114. "snicker":    [7,0,""],
  115. "smirk":    [7,0,""],
  116. "jump":        [0,["in aggravation"] ," jump$ up and down \nHOW"],
  117. "fume":        [7,0 ,""],
  118. "faint":    [7,0,""],
  119. "shrug":    [7,0,""],
  120. "pout":        [7,0,""],
  121. "hiccup":    [7,0,""],
  122. "frown":    [7,0,""],
  123. "gasp":        [7,["in astonishment"],""],
  124. "think":    [7,["carefully"],""],
  125. "ponder":    [7,["over some problem"],""],
  126. "clap":        [7,0,""],
  127. "sigh":        [7,0,""],
  128. "cough":    [7,["noisily"],""],
  129. "shiver":    [7,["from the cold"],""],
  130. "blush":    [6,0," blush \nHOW"," blushes \nHOW"]
  131. }
  132.  
  133.  
  134. # stripcrlf: strip CR/LF or LF from string
  135. def stripcrlf(str):
  136.     if len(str)>2:
  137.         if (str[-2:]=='\r\n') or (str[-2:]=='\n\r'): return str[:-2]
  138.     if len(str)>1:
  139.         if str[-1]=='\n': return str[:-1]
  140.     return str
  141.  
  142. # broadcast: send a msg to all connections, except self.
  143. def broadcast(self_idx,msg):
  144.     i=0
  145.     for c in connections:
  146.         if i!=self_idx: c.sock.send(msg)
  147.         i=i+1
  148.  
  149. # shutdown: close connection & send msg to quitter & others
  150. def shutdown(i,msg,brmsg):
  151.     if brmsg: broadcast(i,brmsg)
  152.     connections[i].send(msg)
  153.     log(i,"QUIT")
  154.     del connections[i]
  155.  
  156. # log: log message from connection #i (to screen)
  157. def log(i,msg):
  158.     if connections[i].name:
  159.         print msg+'\t'+connections[i].name+'\t'+`connections[i].address`,time.ctime(time.time())
  160.     else:
  161.         print msg+"\t<???>\t"+`connections[i].address`,time.ctime(time.time())
  162.  
  163. # mkerr: convert string into error string
  164. # mknote: convert string into note string
  165. def mkerr(msg):
  166.     return "** ERROR: "+msg+" **\r\n"
  167. def mknote(msg):
  168.     return "** NOTE: "+msg+" **\r\n"
  169.  
  170. # do_feeling: execute feeling 'data'. i=index of active socket.
  171. def do_feeling(i,data):
  172.     line=string.split(data)
  173.     feeling=string.lower(line[0])[1:]
  174.     if len(line)>1:
  175.         args=line[1:]
  176.     else:
  177.         args=None
  178.  
  179.     if feeling in feelings.keys():
  180.         adv=""
  181.         if feelings[feeling][1]:
  182.             adv=' '+feelings[feeling][1][0]
  183.         connections[i].send("You "+feeling+adv+".\r\n")
  184.         if feeling[-2:]=='ch' or feeling[-1]=='s' or feeling[-2:]=='sh':
  185.             feeling=feeling+'e'
  186.         if feeling[-1]=='y':
  187.             feeling=feeling[:-1]+"ie"
  188.         broadcast(i,connections[i].name+' '+feeling+'s'+adv+".\r\n")
  189.     else:
  190.         connections[i].senderr("Unknown feeling")
  191.  
  192.  
  193. # do_command: execute command 'data'. i=index of active socket.
  194. def do_command(i,data):
  195.     line=string.split(data)
  196.     cmd=string.lower(line[0])[1:]
  197.     args=line[1:]
  198.  
  199.     if cmd=='name':
  200.         if not args:
  201.             connections[i].senderr("No name")
  202.         elif args[0] in map(lambda x:x.name,connections):
  203.             connections[i].senderr("Name already in use")
  204.         else:
  205.             broadcast(-1,mknote(connections[i].name+" now known as: "+args[0]))
  206.             connections[i].name=args[0]
  207.     elif cmd=="who":
  208.         str=""
  209.         for c in connections:
  210.             str=str+"  "+c.name+"\r\n"
  211.         connections[i].send(str)
  212.     elif cmd=="quit":
  213.         shutdown(i,"Quitting chat service.\n",mknote(connections[i].name+" LEFT"))
  214.     elif cmd=="help":
  215.         if args and args[0]=="feelings":
  216.             connections[i].send("** FEELINGS **\r\n"
  217.             "Currently, the following feeling commands have been defined:\r\n")
  218.             s=""
  219.             fls=feelings.keys()
  220.             fls.sort()
  221.             for f in fls:
  222.                 if (len(s)+len(f))>=76:
  223.                     connections[i].send(s+"\r\n")
  224.                     s=""
  225.                 s=s+f+' '
  226.             connections[i].send(s+"\n")
  227.         else:
  228.             connections[i].send("** CHAT SERVICE ONLINE HELP **\r\n"
  229.             "All normal text entered is sent to all other people, as if you 'said' it.\r\n"
  230.             "Lines starting with ' are a special feelings line. See @help feelings.\r\n"
  231.             "Lines starting with @ are regarded as a special command line:\r\n"
  232.             "@help topic - help on topic\r\n"
  233.             "@who      - who's there\r\n"
  234.             "@name foo - set your name to 'foo'\r\n"
  235.             "@quit     - exit chat service\r\n")
  236.     else:
  237.         connections[i].senderr("Bad CMD, try @help")
  238.  
  239.  
  240. def serverloop(host,port):
  241.     global feelings
  242.  
  243.     print "\nStarting telnet chat service on",host,port,"..."
  244.  
  245.     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  246.     s.bind(host, port)
  247.     s.listen(1)
  248.  
  249.     while 1:
  250.         socklist=map(lambda x:x.sock, connections)
  251.         ins,outs,exs = select.select(socklist+[s],[],[],5)
  252.  
  253.         for sock in ins:
  254.             if sock==s:
  255.                 sock,addr=s.accept()
  256.                 connections.append(connection(sock,addr))
  257.                 sock.send(WelcomeMsg+"Enter your name: ")
  258.  
  259.             else:
  260.                 i = socklist.index(sock)
  261.                 data=sock.recv(512)
  262.                 if data:
  263.                     data=string.strip(stripcrlf(data))
  264.  
  265.                     # If we don't have a name yet, read the name.
  266.                     if not connections[i].name:
  267.                         if len(data)<2:
  268.                             shutdown(i,mkerr("Name too short, try again"),None)
  269.                         elif data in map(lambda x:x.name,connections):
  270.                             shutdown(i,mkerr("Name already in use, try again"),None)
  271.                         else:
  272.                             connections[i].name=data
  273.                             broadcast(i,mknote(connections[i].name+" ARRIVES"))
  274.                             connections[i].sendnote("Ok, enter chatter or @help for help")
  275.                             log(i,"CONNECT")
  276.                             
  277.                     elif data:
  278.                         if data[0]=='\'':
  279.                             do_feeling(i,data)
  280.                         elif data[0]=='@':
  281.                             do_command(i,data)
  282.                         else:
  283.                             # normal chatter
  284.                             broadcast(i,connections[i].name+" says: `"+data+"'\r\n")
  285.  
  286.                     if sock.fileno()>0: sock.send("> ")
  287.  
  288.                 else:
  289.                     # EOF read or closed connection.
  290.                     shutdown(i,"Quitting chat service.\r\n",mknote(connections[i].name+" LEAVES"))
  291.  
  292.  
  293. def main(host,port):
  294.     global connections
  295.  
  296.     serverloop(host,port)
  297.     print "Server shut down."
  298.     del connections            # close all connections
  299.  
  300.  
  301. if __name__=='__main__':
  302.     argc=len(sys.argv)
  303.     if argc==1:
  304.         main(socket.gethostname(),9999)
  305.     elif argc==2:
  306.         main(socket.gethostname(),eval(sys.argv[1]))
  307.     elif argc==3:
  308.         main(host=sys.argv[1],port=eval(sys.argv[2]))
  309.     else:
  310.         print "Usage: python",sys.argv[0],"[hostname] [portnumber]"
  311.