]> gitweb.pimeys.fr Git - bots/basile.git/blobdiff - basile.py
Basile sera plus poli
[bots/basile.git] / basile.py
index 14769c36df39f086feb371bf84601985faf20fdc..0e1f62ce132d2cf0bfa84cdffa75091b4eb6887c 100755 (executable)
--- a/basile.py
+++ b/basile.py
@@ -32,8 +32,8 @@ config_logfile_template="basile.%s.log"
 def get_config_logfile(serveur):
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
     return config_logfile_template%(serveurs[serveur])
-config_overops=["[20-100]","[20-100]_"]
-config_ops=["PEB","Nit"]
+config_overops=["[20-100]","[20-100]_", "PEB"]
+config_ops=["Nit"]
 config_report_bugs_to=["[20-100]"]
 
 config_insultes=[u"conna(rd|sse)",u"pute",u"con(|ne)",u"enf(oiré|lure)",
@@ -49,16 +49,15 @@ u"naze",u"truie",u"iconoclaste",
 u"peigne(-|)cul",u"ignare",u"illétré",u"lèche(|-)cul",u"malotru",u"motherfucker",u"nabot",u"nigaud",
 u"nul",u"escroc",u"pouffiasse",u"pourriture",u"raclure",u"relou",u"sagouin",u"putain",
 u"péripatéticienne"]
-config_insultes_answers=[u"toi-même",
+config_insultes_answers=[
 u"Oh non ! Quelle insulte ! Je crois que je ne m'en reléverai jamais…\nAh si, ça y est.",
-u"J'entends comme un vague murmure, tu disais ?",
+u"J'entends comme un vague murmure, vous disiez ?",
 u"Je vais prendre ça pour un compliment.",
-u"Tu sais, pour toi c'est peut-être une insulte, mais pour moi ce n'est qu'une suite de 0 et de 1…",
-u"Si tu allais voir sur un autre chan si j'y suis ?",
-u"Permets-moi de te retourner le compliment.",
-u"Mais je ne te permets pas !"]
+u"Vous savez, pour vous c'est peut-être une insulte, mais pour moi ce n'est qu'une suite de 0 et de 1…",
+u"Permettez-moi de vous retourner le compliment.",
+u"Mais je ne vous permets pas !"]
 
-config_gros=[u"gros"]
+config_gros=[u"gros",u"énorme",u"lourd"]
 
 config_buffer_fail_answers=["haha !","You type like you drive","encore un effort ;)"]
 
@@ -82,43 +81,53 @@ config_time_between_perdu_trigger=3600*3 #temps moyen pour perdre en l'absence d
 config_time_between_perdu_trigger_delta = 30*60 #marge autorisée autour de ^^^
 config_time_between_perdu=30*60 #temps pendant lequel on ne peut pas perdre
 
-config_tag=[u"t(|a)g",u"ta gueule",u"la ferme",u"ferme( |-)la",u"tais-toi",u"chut"]
-config_tag_actions=[u"se tait",u"ferme sa gueule",u"se la ferme",u"la ferme"]
+config_tag_triggers=[u"t(|a)g",u"ta gueule",u"la ferme",u"ferme( |-)la",u"tais-toi",u"chut"]
+config_tag_actions=[u"se tait",u"se tient coi"]
 config_tag_answers=[u"J'me tais si j'veux !",
 u"Je t'entends pas :°",
 u"Héhé, try again",
 u"Non, j'ai pas envie",
 u"Peut-être quand toi tu la fermeras, et encore…"]
 
-config_tesla=[u"t('|u )es là \?",u"\?",u"plop \?",u"plouf \?"]
+config_tesla_triggers=[u"t('|u )es là \?",u"\?",u"plop \?",u"plouf \?"]
 config_tesla_answers=[u"Oui, je suis là",u"Oui ?",u"En quoi puis-je me rendre utile ?"]
 config_tesla_actions=[u"est là",u"attend des instructions",u"is alive"]
 
-config_compliment=[u"gentil",u"cool",u"sympa"]
+config_compliment_triggers=[u"gentil",u"cool",u"sympa"]
 config_compliment_answers=[u"Merci, c'est gentil :)",u"Je te retourne le compliment",u"C'est gentil ça."]
 
-config_merci=[u"merci",u"remercie",u"thx",u"thank(|s)"]
+config_merci_triggers=[u"merci",u"remercie",u"thx",u"thank(|s)"]
 config_merci_answers=[u"Mais de rien.",u"À ton service ;)",u"Quand tu veux ^^",
 u"Tout le plaisir est pour moi."]
 
-config_tamere=[u"ta mère"]
+config_tamere_triggers=[u"ta mère"]
 config_tamere_answers=[u"Laisse ma mère en dehors de ça !",
 u"Tu veux qu'on parle de ta soœur ?",
 u"Et la tienne ?",
 u"Ce que fait ma mère c'est comme ce que tu fais avec ta bite, ça nous regarde pas…",
-u"♩ J'ai vu ta mère sur chat rouleeeeeeette ♫"
+u"♩ J'ai vu ta mère sur chat rouleeeeeeette ♫",
 u"On avait dit \"pas les mamans\""]
 
-config_action_trigger=[u"(frappe|cogne|tape)(| sur)",u"démolit",u"vomit sur",u"slap(|s)"]
-config_action_answers=[u"Hey ! Mais qu'est-ce que j'ai fait ?",
+config_bad_action_triggers=[u"(frappe|cogne|tape)(| sur)",u"(démolit|dégomme|fouette|agresse)",
+u"vomit sur",u"slap(|s)"]
+config_bad_action_answers=[u"Hey ! Mais qu'est-ce que j'ai fait ?",
 u"Pourquoi moi ?",
 u"Mais euh…",
 u"Mais j'ai rien demandé moi !"]
-config_action_actions=[u"prend de la distance, par précaution…",u"part en courant"]
+config_bad_action_actions=[u"prend de la distance, par précaution…",u"part en courant",u"esquive"]
 
-config_bonjour=[u"(s|)(a|'|)lu(t|)",u"hello",u"plop",u"plip",u"pr(ou|ü)t",u"bonjour",u"bonsoir"]
+config_good_action_triggers=[u"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u"embrasse",u"c(?:â|a)line",u"caresse"]
+config_good_action_answers=[u"owi \o/",u"c'est gentil ! ♡"]
+config_good_action_actions=[u"ronronne",u"est content"]
+
+config_bonjour_triggers=[u"(s|)(a|'|)lu(t|)",u"hello",u"pl(o|i)p",u"pr(ou|ü)t",u"bonjour",u"bonsoir",u"coucou"]
 config_bonjour_answers=[u"Salut {}",u"Hello {} :)",u"Bonjour {}",u"Hello {}",u"{}: hello",u"{}: bonjour"]
 
+config_bonne_nuit_triggers=[u"bonne nuit",u"'?nite",u"'?nuit",u"'?night",u"good night",u"'?nunuit"]
+config_bonne_nuit_answers=[u"{}: sweet dreams ;)",u"Bonne nuit {} !",u"À demain {}. :)","{}: dors bien ^^"]
+
+config_kick_answers=[u"Ben qu'est-ce que j'ai fait ? :(",u"Mais euh, j'ai rien fait de mal…","{} a le /kick facile :)"]
+config_kick_actions=[u"se tiendra à carreaux",u"essaiera de ne plus provoquer les foudres de {}"]
 
 config_thisfile= os.path.realpath( __file__ )
 def get_filesize():
@@ -178,9 +187,13 @@ def connect_NK():
 
 def login_NK(username,password,typ="bdd"):
     sock=connect_NK()
+    if typ=="special": # ça c'est pour Basile lui-même
+        masque='["note"]'
+    elif typ=="bdd":
+        masque='[["all"],["all"],false]'
     try:
         # Basile a un compte special user
-        commande='login [%s,%s,"%s"]'%(json.dumps(username),json.dumps(password),typ)
+        commande='login [%s,%s,"%s",%s]'%(json.dumps(username),json.dumps(password),typ,masque)
         sock.write(commande)
         out=sock.read()
     except Exception as exc:
@@ -190,23 +203,22 @@ def login_NK(username,password,typ="bdd"):
     return json.loads(out),sock
 
 
-def is_something(chain,matches,avant=u".*(^| )",apres=u"($|\.| |,|;).*",case_sensitive=False,debug=False):
+def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
     if case_sensitive:
         chain=unicode(chain,"utf8")
     else:
         chain=unicode(chain,"utf8").lower()
     allmatches="("+"|".join(matches)+")"
     reg=(avant+allmatches+apres).lower()
-    if re.match(reg,chain):
-        return True
-    return False
+    o=re.match(reg,chain)
+    return o
 
 def is_insult(chain,debug=True):
-    return is_something(chain,config_insultes,avant=".*(^| |')")
+    return is_something(chain,config_insultes,avant=".*(?:^| |')")
 def is_not_insult(chain):
     chain=unicode(chain,"utf8")
     insult_regexp=u"("+u"|".join(config_insultes)+u")"
-    middle_regexp=u"(un(|e) ((putain|enfoiré) d(e |'))*|)(| super )( (gros|petit|grand|énorme) |)"
+    middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
     reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
     if re.match(reg,chain):
         return True
@@ -215,19 +227,27 @@ def is_not_insult(chain):
 def is_perdu(chain):
     return is_something(chain,config_perdu)
 def is_tag(chain):
-    return is_something(chain,config_tag)
+    return is_something(chain,config_tag_triggers)
 def is_gros(chain):
     return is_something(chain,config_gros)
 def is_tesla(chain):
-    return is_something(chain,config_tesla,avant=u"^",apres=u"$",debug=True)
+    return is_something(chain,config_tesla_triggers,avant=u"^",apres=u"$",debug=True)
 def is_merci(chain):
-    return is_something(chain,config_merci)
+    return is_something(chain,config_merci_triggers)
 def is_tamere(chain):
-    return is_something(chain,config_tamere)
-def is_action_trigger(chain,pseudo):
-    return is_something(chain,config_action_trigger,avant=u"^",apres=" %s($|\.| |,|;).*"%(pseudo))
+    return is_something(chain,config_tamere_triggers)
+def is_bad_action_trigger(chain,pseudo):
+    return is_something(chain,config_bad_action_triggers,avant=u"^",
+                            apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
+def is_good_action_trigger(chain,pseudo):
+    return is_something(chain,config_good_action_triggers,avant=u"^",
+                            apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
+def is_bonjour(chain):
+    return is_something(chain,config_bonjour_triggers,avant=u"^")
+def is_bonne_nuit(chain):
+    return is_something(chain,config_bonne_nuit_triggers,avant=u"^")
 def is_pan(chain):
-    return re.match(u"^(pan|bim|bang)$",unicode(chain,"utf8").lower().strip())
+    return re.match(u"^(pan|bim|bang) .*$",unicode(chain,"utf8").lower().strip())
 
 
       
@@ -293,6 +313,7 @@ class Basile(ircbot.SingleServerIRCBot):
         serv.nick(config_irc_pseudo)
     
     def on_welcome(self, serv, ev):
+        self.serv=serv # ça serv ira :)
         self.give_me_my_pseudo(serv)
         serv.privmsg("NickServ","identify %s"%(config_irc_password))
         log(self.serveur,"Connected")
@@ -377,9 +398,9 @@ class Basile(ircbot.SingleServerIRCBot):
                                                (auteur,fille,verbe)).encode("utf8"))
     def pourmoi(self, serv, message):
         """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
-        pseudo=serv.get_nickname()
+        pseudo=self.nick
         size=len(pseudo)
-        if message[:size]==pseudo and message[size]==":":
+        if message[:size]==pseudo and len(message)>size and message[size]==":":
             return (True,message[size+1:].lstrip(" "))
         else:
             return (False,message)
@@ -428,15 +449,18 @@ class Basile(ircbot.SingleServerIRCBot):
  HELP       Affiche de l'aide sur une commande.
  CONNECT    Ouvre une connection au serveur Note Kfet.
  IDENTIFY   Me permet de savoir qui tu es sur la note kfet.
- DROP       Me fait oublier ton identité."""
+ DROP       Me fait oublier ton identité.
+ SOLDE      Obtenir ton solde"""
             helpmsg_ops="""
  JOIN       Faire rejoindre un chan
  LEAVE      Faire quitter un chan
  QUIET      Se taire sur un chan
  NOQUIET    Opposé de QUIET
- LOST       Perdre sur un chan"""
+ LOST       Perdre sur un chan
+ SOLDE <pseudo>  Donner le solde de quelqu'un"""
             helpmsg_overops="""
- SAY        Fais envoyer un message sur un chan ou à une personne
+ SAY        Fait envoyer un message sur un chan ou à une personne
+ DO         Fait faire une action sur un chan
  STAY       Ignorera les prochains LEAVE pour un chan
  NOSTAY     Opposé de STAY
  DIE        Mourir"""
@@ -582,6 +606,22 @@ class Basile(ircbot.SingleServerIRCBot):
                 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
             else:
                 notunderstood=True
+        elif cmd=="do":
+            if auteur in self.overops and len(message)>2:
+                serv.action(message[1]," ".join(message[2:]))
+                log(self.serveur,"priv",auteur," ".join(message))
+            elif len(message)<=2:
+                serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
+            else:
+                notunderstood=True
+        elif cmd=="kick":
+            if auteur in self.overops and len(message)>2:
+                serv.kick(message[1],message[2]," ".join(message[3:]))
+                log(self.serveur,"priv",auteur," ".join(message))
+            elif len(message)<=2:
+                serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo>")
+            else:
+                notunderstood=True
         elif cmd=="lost":
             if auteur in self.ops and len(message)>1:
                 serv.privmsg(message[1],"J'ai perdu !")
@@ -590,6 +630,33 @@ class Basile(ircbot.SingleServerIRCBot):
                 serv.privmsg(auteur,"Syntaxe : LOST <channel>")
             else:
                 notunderstood=True
+        elif cmd=="solde":
+            if len(message)==1:
+                if self.identities.has_key(auteur):
+                    try:
+                        self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(self.identities[auteur])))
+                        ret=json.loads(self.nk.read())
+                        solde=ret["msg"][0]["solde"]
+                        pseudo=ret["msg"][0]["pseudo"]
+                    except Exception as exc:
+                        print exc
+                        serv.privmsg(auteur,"failed")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                        return
+                    serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
+                else:
+                    serv.privmsg(canal,"Je ne connais pas ton pseudo note.")
+            elif auteur in self.ops:
+                try:
+                    self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(message[1])))
+                    ret=json.loads(self.nk.read())
+                    solde=ret["msg"][0]["solde"]
+                    pseudo=ret["msg"][0]["pseudo"]
+                except Exception as exc:
+                    serv.privmsg(auteur,"failed")
+                    log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                    return
+                serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
         else:
             notunderstood=True
         if notunderstood:
@@ -602,8 +669,9 @@ class Basile(ircbot.SingleServerIRCBot):
         try:
             test=bot_unicode(message)
         except UnicodeBotError:
-            serv.privmsg(canal,
-              "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
+            if not canal in self.quiet_channels:
+                serv.privmsg(canal,
+                  "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
             return
         pour_moi,message=self.pourmoi(serv,message)
         if pour_moi and message.split()!=[]:
@@ -625,6 +693,8 @@ class Basile(ircbot.SingleServerIRCBot):
                                            or auteur in self.overops):
                     serv.part(canal,message="Éjecté par %s"%(auteur))
                     log(self.serveur,canal,auteur,message+"[successful]")
+                    if canal in self.chanlist:
+                        self.chanlist.remove(canal)
                 else:
                     serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
                     log(self.serveur,canal,auteur,message+"[failed]")
@@ -655,8 +725,8 @@ class Basile(ircbot.SingleServerIRCBot):
                     serv.nick(become)
                     log(self.serveur,canal,auteur,message+"[successful]")
     
-            elif cmd in ["coucou"] and not canal in self.quiet_channels:
-                serv.privmsg(canal,"%s: coucou"%(auteur))
+            if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
+                serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
             elif cmd in ["ping"] and not canal in self.quiet_channels:
                 serv.privmsg(canal,"%s: pong"%(auteur))
 
@@ -694,16 +764,17 @@ class Basile(ircbot.SingleServerIRCBot):
                     answer=random.choice(config_insultes_answers)
                     for ligne in answer.split("\n"):
                         serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
-            if is_gros(message) and not canal in self.quiet_channels:
+            gros_match=is_gros(message)
+            if gros_match and not canal in self.quiet_channels:
                 taille=get_filesize()
-                answer=u"Mais non, je ne suis pas gros, %sKo tout au plus…"%(taille)
+                answer=u"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match.groups()[0],taille)
                 serv.privmsg(canal,"%s: %s"%(auteur,answer.encode("utf8")))
             if is_tesla(message) and not canal in self.quiet_channels:
                 l1,l2=config_tesla_answers,config_tesla_actions
                 n1,n2=len(l1),len(l2)
                 i=random.randrange(n1+n2)
                 if i>=n1:
-                    serv.action(canal,l2[i-n1])
+                    serv.action(canal,l2[i-n1].encode("utf8"))
                 else:
                     serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
             if is_tamere(message) and not canal in self.quiet_channels:
@@ -723,7 +794,7 @@ class Basile(ircbot.SingleServerIRCBot):
                 answer=random.choice(config_merci_answers)
                 for ligne in answer.split("\n"):
                     serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
-            out=re.match(u"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
+            out=re.match(ur"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
                          unicode(message.upper(),"utf8"))
             if out and not canal in self.quiet_channels:
                 out=out.groups()[0]
@@ -744,18 +815,20 @@ class Basile(ircbot.SingleServerIRCBot):
                     serv.privmsg(canal,"%s: pfff, j'ai l'air malin maintenant… [ ?"%(auteur))
                 elif out in "[\\":
                     serv.privmsg(canal,"%s: nan mais il faut qu'on arrête, ça va finir par poser des problèmes…"%(auteur))
-                elif re.match(r"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out):
+                elif re.match(ur"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out):
                     def translate(mess):
                         return "".join([{u"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i]:u"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i]
                                         for i in range(20)}[j]
                                        for j in mess])
                     out=int(translate(out))
                     serv.privmsg(canal,"%s: %s !"%(auteur,translate(str(out+1)).encode("utf8")))
-            if (not canal in self.quiet_channels
-                and re.match((u"^("+"|".join(config_bonjour)+").*").lower(),message.lower()) ):
+            if is_bonjour(message) and not canal in self.quiet_channels:
                 answer=random.choice(config_bonjour_answers)
                 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
-            if is_pan(message):
+            if is_bonne_nuit(message) and not canal in self.quiet_channels:
+                answer=random.choice(config_bonne_nuit_answers)
+                serv.privmsg(canal,answer.format(auteur).encode("utf8"))
+            if is_pan(message) and not canal in self.quiet_channels:
                 serv.privmsg(canal,"%s: c'est pas sur moi qu'il faut tirer !"%(auteur))
         else:
             if message in ["!pain au chocolat","!chocolatine"] and not canal in self.quiet_channels:
@@ -771,8 +844,8 @@ class Basile(ircbot.SingleServerIRCBot):
                 serv.privmsg(canal,"%s: %s"%(auteur,answer))
             if not canal in self.quiet_channels:
                 self.try_tamere(serv,canal,auteur,message)
-                mypseudo=serv.get_nickname()
-                if re.match((u"^("+u"|".join(config_bonjour)
+                mypseudo=self.nick
+                if re.match((u"^("+u"|".join(config_bonjour_triggers)
                                   +u")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
                              ).format(mypseudo).lower(), message.strip().lower()):
                     answer=random.choice(config_bonjour_answers)
@@ -791,15 +864,52 @@ class Basile(ircbot.SingleServerIRCBot):
         action = ev.arguments()[0]
         auteur = irclib.nm_to_n(ev.source())
         channel = ev.target()
-        mypseudo=serv.get_nickname()
-        if is_action_trigger(action,mypseudo):
-            l1,l2=config_action_answers,config_action_actions
+        try:
+            test=bot_unicode(action)
+        except UnicodeBotError:
+            serv.privmsg(channel,
+              "%s : Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
+            return
+        mypseudo=self.nick
+        
+        if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
+            l1,l2=config_bad_action_answers,config_bad_action_actions
             n1,n2=len(l1),len(l2)
             i=random.randrange(n1+n2)
             if i>=n1:
-                serv.action(channel,l2[i-n1])
+                serv.action(channel,l2[i-n1].encode("utf8"))
             else:
                 serv.privmsg(channel,"%s: %s"%(auteur,l1[i].encode("utf8")))
+        if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
+            l1,l2=config_good_action_answers,config_good_action_actions
+            n1,n2=len(l1),len(l2)
+            i=random.randrange(n1+n2)
+            if i>=n1:
+                serv.action(channel,l2[i-n1].encode("utf8"))
+            else:
+                serv.privmsg(channel,"%s: %s"%(auteur,l1[i].encode("utf8")))
+    
+    def on_kick(self,serv,ev):
+        auteur = irclib.nm_to_n(ev.source())
+        channel = ev.target()
+        victime = ev.arguments()[0]
+        raison = ev.arguments()[1]
+        if victime==self.nick:
+            log(self.serveur,"%s kické par %s (raison : %s)" %(victime,auteur,raison))
+            time.sleep(2)
+            serv.join(channel)
+            l1,l2=config_kick_answers,config_kick_actions
+            n1,n2=len(l1),len(l2)
+            i=random.randrange(n1+n2)
+            if i>=n1:
+                serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
+            else:
+                serv.privmsg(channel,"%s: %s"%(auteur,l1[i].format(auteur).encode("utf8")))
+
+    def _getnick(self):
+        return self.serv.get_nickname()
+    nick=property(_getnick)
+
 
 if __name__=="__main__":
     import sys