triggerbot.py 197 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260
  1. # Copyright (c) 2013 Sylvia van Os
  2. # Copyright (c) 2013 Joshua Phillips
  3. # Based on ircLogBot.py, which is copyright (c) Twisted Matrix Laboratories.
  4. # This file is part of Triggerbot, released under the MIT license
  5. #
  6. # Permission is hereby granted, free of charge, to any person obtaining a copy
  7. # of this software and associated documentation files (the "Software"), to deal
  8. # in the Software without restriction, including without limitation the rights
  9. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. # copies of the Software, and to permit persons to whom the Software is
  11. # furnished to do so, subject to the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be included in
  14. # all copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. # THE SOFTWARE.
  23. """
  24. Triggerbot
  25. An IRC bot made to help assist keeping a channel safe for those
  26. with triggers.
  27. The bot quitely monitors the channel, until it sees something
  28. wrong. If someone says the bot's name in the channel followed
  29. by a ":", or if someone speaks to the bot in a private query
  30. window, the bot will assume it to be a command and reply to it
  31. as such.
  32. Do not run this script directly. Instead, run main.py with at least
  33. a server name and port number. For example:
  34. $ python main.py --server irc.example.com --port 6667
  35. will connect to irc.example.com on channel 6667.
  36. For more information, please check the README.
  37. """
  38. from twisted.words.protocols import irc
  39. from twisted.internet import reactor, protocol, task
  40. from twisted.python import log
  41. import time
  42. import datetime
  43. import sys
  44. import re
  45. import random
  46. import os.path
  47. import cPickle
  48. import xapian
  49. import bcrypt
  50. import reloading
  51. class UserError(Exception):
  52. pass
  53. class UserNotFound(UserError):
  54. def report(self, bot, user, recipient):
  55. bot.send_and_log(recipient, user,
  56. "Sorry, I don't know who %r is." % self.message)
  57. class MessageNotFound(UserError):
  58. def report(self, bot, user, recipient):
  59. bot.send_and_log(recipient, user,
  60. "Sorry, I couldn't find message %s." % self.message)
  61. class TopicNotFound(UserError):
  62. def report(self, bot, user, recipient):
  63. bot.send_and_log(recipient, user,
  64. "Sorry, there's no such topic %r." % self.message)
  65. class ChannelNotFound(UserError):
  66. def report(self, bot, user, recipient):
  67. bot.send_and_log(recipient, user,
  68. "Sorry, I don't know channel %s." % self.message)
  69. class OwnerNotFound(UserError):
  70. def report(self, bot, user, recipient):
  71. bot.send_and_log(recipient, user,
  72. "Sorry, I don't know who owns this channel %s." % self.message)
  73. class CommandNotFound(UserError):
  74. def report(self, bot, user, recipient):
  75. bot.send_and_log(recipient, user,
  76. "Sorry, there is no such command %r." % self.message)
  77. class BadValue(UserError):
  78. def report(self, bot, user, recipient):
  79. bot.send_and_log(recipient, user,
  80. "Invalid value: %r." % self.message)
  81. class BadCommand(Exception):
  82. pass
  83. class MissingParams(UserError):
  84. def report(self, bot, user, recipient):
  85. bot.send_and_log(recipient, user,
  86. "Sorry, but that command needs more parameters.")
  87. class WrongParamCount(UserError):
  88. def report(self, bot, user, recipient):
  89. bot.send_and_log(recipient, user,
  90. "Sorry, but you entered a wrong amount of parameters.")
  91. class AuthenticationError(UserError):
  92. def report(self, bot, user, recipient):
  93. bot.send_and_log(recipient, user,
  94. "Sorry, but you need to be logged in to do that.")
  95. def join_and(delim, last_delim, items):
  96. '''join_and(", ", " and ", ["one", "two", "three"]) == "one, two and three"'''
  97. items = list(items)
  98. if len(items) > 1:
  99. return delim.join(items[:-1]) + last_delim + items[-1]
  100. elif len(items) == 1:
  101. return items[0]
  102. else:
  103. return ""
  104. def parse_bool(s):
  105. if s.lower() in ("y", "yes", "on", "enabled", "true"): return True
  106. elif s.lower() in ("n", "no", "off", "disabled", "false"): return False
  107. else:
  108. raise BadValue(s)
  109. class MessageLogger:
  110. """
  111. An independent logger class (because separation of application
  112. and protocol logic is a good thing).
  113. """
  114. def __init__(self, file):
  115. self.file = file
  116. def log(self, message):
  117. """Write a message to the file."""
  118. timestamp = time.strftime("[%H:%M:%S]", time.localtime(time.time()))
  119. self.file.write('%s %s\n' % (timestamp, message))
  120. self.file.flush()
  121. def is_channel_name(name):
  122. return name[0] in irc.CHANNEL_PREFIXES
  123. class Setting(reloading.Reloadable):
  124. """Class containing bot settings."""
  125. is_setting = True
  126. def __init__(self, name):
  127. self.name = name
  128. self.channels = []
  129. self.globalmotd = ""
  130. self.maindisabled = False
  131. self.disabledcommands = []
  132. def __str__(self):
  133. return self.name
  134. class Channel(reloading.Reloadable):
  135. """Information about a channel the bot is in."""
  136. is_channel = True
  137. def __init__(self, name):
  138. self.name = name
  139. self.admins = []
  140. self.users = set()
  141. self.rules = {}
  142. self.topic = None
  143. self.topicset = None
  144. self.mode = []
  145. self.prevmode = []
  146. self.blockedtopics = {}
  147. def __str__(self):
  148. return self.name
  149. class User(reloading.Reloadable):
  150. """Information about a user."""
  151. is_user = True
  152. def __init__(self, nick):
  153. self.nick = nick
  154. self.host = ""
  155. self.admin = 0 # 0: Not an admin; 1: Main admin (claimadmin); 2: Additional admin (admin add)
  156. self.admincommandsallowed = []
  157. self.master = None
  158. self.alts = []
  159. self.friends = []
  160. self.trusts = [] # Lists of users having access to settings on this account
  161. self.topics = {}
  162. self.trigger_words = set()
  163. self.helped = False
  164. self.listenmode = False
  165. self.channel = False # Defines if the user has its own channel or not
  166. self.channelallow = [] # Which nicknames which are not alts are allowed in your own channel
  167. self.away = False
  168. self.ignore = [] # The user's ignore list
  169. self.ignoredby = [] # Who is ignoring this user
  170. self.ignored = False # Defines if the bot ignores all commands of this user
  171. self.awaycheck = True
  172. self.autologout = True
  173. self.autosilence = True # Silence the channel when away
  174. self.hideown = False # Hide own triggers in triggersafe channel topic and rules
  175. self.logged_in = False
  176. self.nickservlogin = True # Log the user in if the "r" flag is set
  177. self.motdread = False
  178. self.password = None
  179. self.seen = datetime.datetime.now()
  180. self.lastlogout = datetime.datetime.now()
  181. self.autopurge = True # Automatically purge this user if not online for 30 days
  182. self.messages = {}
  183. self.readmessages = []
  184. self.messagestoretime = 7 # Store messages for a week by default
  185. self.warnings = {}
  186. self.logs = {} # Recent logged command executed
  187. def __str__(self):
  188. return self.nick
  189. def __repr__(self):
  190. return self.nick
  191. class Topic(reloading.Reloadable):
  192. """Information about a trigger topic."""
  193. def __init__(self, name):
  194. self.name = name
  195. self.descriptions = {}
  196. self.words = {}
  197. self.supersedes = []
  198. class TimeFormat():
  199. """Formats dates. Based S Anand's py-pretty code licensed under the WTFPL"""
  200. def _df(self, seconds, denominator=1, text='', past=True):
  201. if past: return str((seconds + denominator/2)/ denominator) + text + ' ago'
  202. else: return 'in ' + str((seconds + denominator/2)/ denominator) + text
  203. def date(self, time=False, asdays=False):
  204. '''Returns a pretty formatted date.
  205. Inputs:
  206. time is a datetime object or an int timestamp
  207. asdays is True if you only want to measure days, not seconds
  208. short is True if you want "1d ago", "2d ago", etc. False if you want
  209. '''
  210. now = datetime.datetime.now()
  211. if type(time) is int: time = datetime.datetime.fromtimestamp(time)
  212. elif not time: time = now
  213. if time > now: past, diff = False, time - now
  214. else: past, diff = True, now - time
  215. seconds = diff.seconds
  216. days = diff.days
  217. if days == 0 and not asdays:
  218. if seconds < 10: return 'just now'
  219. elif seconds < 60: return self._df(seconds, 1, ' seconds', past)
  220. elif seconds < 120: return past and 'a minute ago' or 'in a minute'
  221. elif seconds < 3600: return self._df(seconds, 60, ' minutes', past)
  222. elif seconds < 7200: return past and 'an hour ago' or'in an hour'
  223. else: return self._df(seconds, 3600, ' hours', past)
  224. else:
  225. if days == 0: return 'today'
  226. elif days == 1: return past and 'yesterday' or'tomorrow'
  227. elif days == 2: return past and 'the day before yesterday' or 'the day after tomorrow'
  228. elif days < 7: return self._df(days, 1, ' days', past)
  229. elif days < 14: return past and 'last week' or 'next week'
  230. elif days < 31: return self._df(days, 7, ' weeks', past)
  231. elif days < 61: return past and 'last month' or 'next month'
  232. elif days < 365: return self._df(days, 30, ' months', past)
  233. elif days < 730: return past and 'last year' or 'next year'
  234. else: return self._df(days, 365, ' years', past)
  235. class TriggerBot(irc.IRCClient, reloading.Reloadable):
  236. """Main TriggerBot code."""
  237. bot_commands = {}
  238. @classmethod
  239. def add_command(cls, description=None):
  240. def register(f):
  241. cls.bot_commands[tuple(f.__name__.strip("_").split("_"))] = (description, f)
  242. return f
  243. if callable(description):
  244. # @add_command case
  245. f, description = description, None
  246. return register(f)
  247. else:
  248. # @add_command(description) case
  249. return register
  250. @classmethod
  251. def command_description(cls, command):
  252. return cls.bot_commands[tuple(command)][0]
  253. @classmethod
  254. def subcommands(cls, command):
  255. """
  256. Iterates over sub-commands of the given command.
  257. @param command: Command or sub-command, given as a list of parts,
  258. e.g. ["set", "flags"]
  259. Returns (subcommand, description) pairs.
  260. """
  261. command = tuple(command)
  262. for k, v in cls.bot_commands.iteritems():
  263. if len(k) == len(command) + 1 and k[:len(command)] == command:
  264. yield k[len(command)], v[0]
  265. def dispatch(self, command, user, reply_to, bypass=False, extend=True):
  266. sentcommand = command
  267. params = command.split()
  268. command = ()
  269. if extend:
  270. for current, param in enumerate(params):
  271. # Reset status
  272. param = param.lower()
  273. checklist = []
  274. found = 0
  275. perfectmatch = False
  276. for commandcheck in self.bot_commands:
  277. append = True
  278. try:
  279. # Check if the previous part of this command match the extended values
  280. for x in range(0, current):
  281. if not commandcheck[x] == params[x]:
  282. append = False
  283. if append and not commandcheck[current] in checklist:
  284. checklist.append(commandcheck[current])
  285. except IndexError:
  286. continue
  287. for compareto in checklist:
  288. if compareto == param:
  289. # Exact match, this must be it
  290. params[current] = compareto
  291. perfectmatch = True
  292. break
  293. elif compareto.startswith(param):
  294. # Ensure a lack of ambiguity
  295. params[current] = compareto
  296. found += 1
  297. if not perfectmatch and found > 1:
  298. self.send_and_log(reply_to, user,
  299. "Sorry, but word %s (%s) is too ambiguous. Please be more precise."
  300. % (current, param))
  301. return
  302. channelknown = False
  303. if params[0] == "channel" and is_channel_name(params[1].lower()):
  304. mainchannel = self.get_channel(params[1].split("_")[0].lower())
  305. params.remove(params[1])
  306. channelknown = True
  307. while params and command + (params[0].lower(),) in self.bot_commands:
  308. command += (params.pop(0).lower(),)
  309. if command:
  310. (_, f) = self.bot_commands[command]
  311. try:
  312. if not channelknown:
  313. try:
  314. mainchannel = self.get_channel(str(reply_to).split("_")[0])
  315. except KeyError:
  316. mainchannel = None
  317. return f(self, params, user, reply_to, mainchannel, bypass)
  318. except UserError, e:
  319. e.report(self, user, reply_to)
  320. except BadCommand:
  321. self.send_and_log(reply_to, user,
  322. "Please use one of the following sub-commands: %s."
  323. % join_and(", ", " or ",
  324. (sorted(subc for subc, _ in self.subcommands(command)))))
  325. else:
  326. self.send_and_log(reply_to, user,
  327. "Sorry, I don't know what you mean by %r. Please use the"
  328. " help command to find out the correct command for what"
  329. " you want to do." % "".join(params[:1]))
  330. def get_channel(self, name):
  331. channel = self.channels.get(name.lower())
  332. if channel is None:
  333. channel = Channel(name.lower())
  334. self.channels[channel.name] = channel
  335. return channel
  336. def get_channel_owner(self, name):
  337. for user in self.users:
  338. if user.lower() == name:
  339. return user
  340. raise OwnerNotFound(name)
  341. def get_user(self, nick, create_if_nonexistent=True):
  342. user = self.users.get(nick)
  343. if create_if_nonexistent:
  344. if user is None:
  345. user = User(nick)
  346. self.users[user.nick] = user
  347. return user
  348. else:
  349. return user if user else None
  350. def find_user(self, nick):
  351. user = self.users.get(nick)
  352. if user is None:
  353. raise UserNotFound(nick)
  354. return user
  355. def find_topic(self, name):
  356. topic = self.topics.get(name)
  357. if topic is None:
  358. raise TopicNotFound(name)
  359. return topic
  360. def get_settings(self):
  361. settings = self.settings.get("triggerbot")
  362. return settings
  363. def changed(self):
  364. self.__dirty = True
  365. def save(self):
  366. if self.__dirty:
  367. with open(self.filename, "w") as f:
  368. cPickle.dump((self.users, self.topics, self.channels, self.settings), f)
  369. self.__dirty = False
  370. self.logger.log("Saved state.")
  371. def load(self):
  372. with open(self.filename, "r") as f:
  373. self.users, self.topics, self.channels, self.settings = cPickle.load(f)
  374. self.__dirty = False
  375. self.check_database()
  376. def check_database(self, recipient=None, user_executed=None):
  377. # Make sure the settings are okay
  378. database_values = Setting(None).__dict__
  379. for entry in database_values:
  380. if not hasattr(self.get_settings(), entry):
  381. setattr(self.get_settings(), entry, getattr(Setting(None), entry))
  382. self.changed()
  383. # Make sure users are okay
  384. database_values = User(None).__dict__
  385. for user in self.users.itervalues():
  386. for entry in database_values:
  387. if not hasattr(user, entry):
  388. setattr(user, entry, getattr(User(None), entry))
  389. self.changed()
  390. deletelist = []
  391. for entry in self.users:
  392. if entry != repr(self.users.get(entry)):
  393. if not recipient and not user_executed:
  394. print "WARNING: Database value for %s wrongly links to %s. This user will be deleted." % (entry, repr(self.users.get(entry)))
  395. else:
  396. self.send_and_log(recipient, user_executed,
  397. "WARNING: Database value for %s wrongly links to %s. This user will be deleted."
  398. % (entry, repr(self.users.get(entry))))
  399. deletelist.append(entry)
  400. for entry in deletelist:
  401. del self.users[entry]
  402. if deletelist:
  403. self.changed()
  404. # Check if there is only one head admin
  405. headadmincount = 0
  406. for user in self.users.itervalues():
  407. if user.admin == 1:
  408. headadmincount += 1
  409. if headadmincount > 1:
  410. if not recipient and not user_executed:
  411. print "WARNING: There is more than one head admin. All head admins are now normal admins. Please let the head admin execute !claimadmin ASAP."
  412. else:
  413. self.send_and_log(recipient, user_executed,
  414. "WARNING: There is more than one head admin. All head admins are now normal admins. Please let the head admin execute !claimadmin ASAP.")
  415. for user in self.users.itervalues():
  416. if user.admin == 1 or user.admin == True:
  417. user.admin = 2
  418. elif user.admin == False:
  419. user.admin = 0
  420. elif not headadmincount:
  421. if not recipient and not user_executed:
  422. print "WARNING: No administrator was found. Please use !claimadmin to claim administrator rights."
  423. else:
  424. self.send_and_log(recipient, user_executed,
  425. "WARNING: No administrator was found. Please use !claimadmin to claim administrator rights.")
  426. # Make sure the topics are okay
  427. database_values = Topic(None).__dict__
  428. for topic in self.topics.itervalues():
  429. for entry in database_values:
  430. if not hasattr(topic, entry):
  431. setattr(topic, entry, getattr(Topic(None), entry))
  432. self.changed()
  433. # Make sure the channels are okay
  434. database_values = Channel(None).__dict__
  435. for channel in self.channels.itervalues():
  436. for entry in database_values:
  437. if not hasattr(channel, entry):
  438. setattr(channel, entry, getattr(Channel(None), entry))
  439. self.changed()
  440. def check_for_master(self, name):
  441. if name.master:
  442. user = self.find_user(name.master)
  443. return user
  444. return name
  445. def join_channel(self, name):
  446. chan = self.get_channel(name)
  447. if not chan.name in self.get_settings().channels:
  448. self.get_settings().channels.append(chan.name)
  449. self.changed()
  450. chan.users = set()
  451. # Register the channel
  452. self.msg('Chanserv',
  453. 'REGISTER %s' % name)
  454. self.join(chan.name)
  455. # If this is a triggersafe channel...
  456. if "_" in name:
  457. channelowner = self.get_channel_owner(name.split("_")[1])
  458. self.mode(name, True, "s") # Make channel secret
  459. self.mode(name, True, "i") # Make channel invite-only
  460. allowstring = "%s!*@*" % channelowner
  461. exceptionstring = "I"
  462. # Add an exception for the bot
  463. # Some servers may need this, even though the bot owns the channel
  464. allowstring += " %s!*@*" % self.nickname
  465. exceptionstring += "I"
  466. # Add alts
  467. for alt in self.find_user(channelowner).alts:
  468. allowstring += " %s!*@*" % alt
  469. exceptionstring += "I"
  470. # Add channelallows
  471. for allowed in self.find_user(channelowner).channelallow:
  472. allowstring += " %s!*@*" % allowed
  473. exceptionstring += "I"
  474. # Add invite-only exceptions
  475. self.mode(name, True, "%s %s" % (exceptionstring, allowstring))
  476. def leave_channel(self, name, unregister=False):
  477. chan = self.get_channel(name)
  478. if chan.name in self.get_settings().channels:
  479. self.get_settings().channels.remove(chan.name)
  480. self.changed()
  481. if chan.name in self.channels.keys():
  482. del self.channels[chan.name]
  483. self.leave(chan.name)
  484. if unregister:
  485. # Unregister the channel
  486. self.msg('Chanserv',
  487. 'DROP %s' % name)
  488. def update_rules(self, channel=None, report=True, changed=False):
  489. if channel is None:
  490. for channel in self.channels.itervalues():
  491. assert channel is not None
  492. self.update_rules(channel=channel, report=report, changed=changed)
  493. else:
  494. basechannel = str(channel).split("_")[0]
  495. try:
  496. if str(channel).split("_")[1]:
  497. triggersafechannel = True
  498. except IndexError:
  499. triggersafechannel = False
  500. new_rules = {}
  501. users = []
  502. for channeltocheck in self.channels:
  503. if channeltocheck.split("_")[0] == basechannel:
  504. for user in self.get_channel(channeltocheck).users:
  505. if user not in users:
  506. users.append(user)
  507. try:
  508. channelowner = self.check_for_master(self.get_user(self.get_channel_owner(str(channel).split("_")[1])))
  509. except IndexError:
  510. channelowner = None
  511. for user in users:
  512. master = self.check_for_master(user)
  513. if master == channelowner and channelowner and channelowner.hideown:
  514. # Channel owner wants own triggers hidden in rules. Hide it.
  515. continue
  516. if not (user.away and master.awaycheck):
  517. for topic, level in master.topics.iteritems():
  518. # If a channel does not allow this topic, kick the user out
  519. try:
  520. if self.get_channel(basechannel).blockedtopics[topic.name] <= level:
  521. self.dispatch(command="channel kick %s The topic you have set is not allowed in this channel" % user, user=user, reply_to=channel, bypass=True)
  522. except KeyError:
  523. pass
  524. new_rules[topic] = max(new_rules.get(topic, 0), level)
  525. if new_rules != channel.rules or (channelowner and channel.prevmode != channel.mode and not ("silent" in channel.prevmode == "silent" in channel.mode)):
  526. channel.rules = new_rules
  527. changed = True
  528. if report:
  529. self.report_rules(channel=channel, changed=changed)
  530. channel.prevmode = channel.mode[:]
  531. def report_rules(self, channel, user=None, recipient=None, report_if_empty=True, changed=False):
  532. if not is_channel_name(str(channel)):
  533. return
  534. try:
  535. channelowner = self.check_for_master(self.get_user(self.get_channel_owner(str(channel).split("_")[1])))
  536. except IndexError:
  537. channelowner = None
  538. norulesbesidesownstring = "There are currently no additional rules besides yours"
  539. if channel.rules:
  540. previousDescription = ""
  541. descriptions = []
  542. rules = {}
  543. topicssuperseded = []
  544. othershiddencount = 0
  545. for topic, level in channel.rules.iteritems():
  546. for entry in topic.supersedes:
  547. topicssuperseded.append(entry)
  548. for topic, level in channel.rules.iteritems():
  549. if not topic.name in topicssuperseded:
  550. if channelowner and channelowner.hideown and topic in channelowner.topics:
  551. othershiddencount += 1
  552. else:
  553. descriptions.append(topic.descriptions[level])
  554. for topicnumber, description in enumerate(sorted(descriptions)):
  555. for wordnumber, word in enumerate(description.split(' ')):
  556. try:
  557. if word != previousDescription.split(' ')[wordnumber]:
  558. if wordnumber > 1:
  559. rules[topicnumber] = ' '.join(description.split(' ')[wordnumber-1:])
  560. else:
  561. rules[topicnumber] = description
  562. break
  563. except IndexError:
  564. rules[topicnumber] = description
  565. break
  566. previousDescription = description
  567. rules = "Current rules: %s.%s" % (("do not %s" % join_and("; ", " or ",
  568. (rules[itemnumber]
  569. for itemnumber, description in rules.iteritems())))
  570. if rules else norulesbesidesownstring,
  571. (" | %s trigger%s set by others have been hidden."
  572. % (othershiddencount, ("(s)"
  573. if othershiddencount >= 2 else "")))
  574. if othershiddencount >= 1 else "")
  575. self.update_topic(channel=channel, replace={"rules": rules})
  576. if changed:
  577. self.send_and_log(recipient or user or channel, user, rules)
  578. elif report_if_empty == True:
  579. if channelowner and channelowner.topics and channelowner.hideown and not "silent" in channel.mode:
  580. rules = "%s." % norulesbesidesownstring
  581. else:
  582. rules = "There are currently no additional rules."
  583. self.update_topic(channel=channel, replace={"rules": rules})
  584. if changed:
  585. self.send_and_log(recipient or user or channel, user,
  586. rules)
  587. def update_topic(self, channel, text=None, replace={}):
  588. if not text:
  589. text = channel.topicset
  590. if text:
  591. replace['globalmotd'] = self.get_settings().globalmotd
  592. self.topic(str(channel))
  593. for entry in channel.__dict__.keys():
  594. if not entry in replace.keys():
  595. replace[entry] = getattr(channel, entry)
  596. for entry in replace.keys():
  597. if replace[entry]:
  598. if isinstance(replace[entry], set):
  599. continue # What the hell should I do with sets?
  600. elif not isinstance(replace[entry], str):
  601. replace[entry] = " ".join(replace[entry])
  602. if entry == "globalmotd" and not replace[entry].startswith("MOTD:"):
  603. replace[entry] = "MOTD: %s" % replace[entry]
  604. elif not replace[entry].startswith("Current %s:" % entry):
  605. replace[entry] = "Current %s: %s" % (entry, replace[entry])
  606. if text.find("[%s]" % entry) != -1:
  607. text = text.split("[%s]" % entry)[0] + "%s | " % replace[entry] + text.split("[%s]" % entry)[1]
  608. # Get rid of things we didn't fill in
  609. while text.find("[") != -1 and text.find("]") != -1:
  610. text = text.split("[")[0] + text.split("]")[1]
  611. # Last cleanups
  612. text = " | ".join(text.split(" | ")[:-1]).lstrip()
  613. if getattr(channel, "topic") != text[:self.supported.getFeature("TOPICLEN")]:
  614. self.topic(str(channel), text)
  615. def connectionMade(self):
  616. irc.IRCClient.connectionMade(self)
  617. self.logger.log("[connected at %s]" %
  618. time.asctime(time.localtime(time.time())))
  619. self.__dirty = False
  620. self.channels = {}
  621. self.users = {}
  622. self.topics = {}
  623. self.settings = {"triggerbot": Setting("triggerbot")}
  624. if os.path.exists(self.filename):
  625. self.load()
  626. else:
  627. print "WARNING: No administrator was found. Please use !claimadmin to claim administrator rights."
  628. def connectionLost(self, reason):
  629. irc.IRCClient.connectionLost(self, reason)
  630. for user in self.users:
  631. self.find_user(user).logged_in = False
  632. self.__dirty = True
  633. self.save()
  634. self.logger.log("[disconnected at %s]" %
  635. time.asctime(time.localtime(time.time())))
  636. def signedOn(self):
  637. """Called when bot has succesfully signed on to server."""
  638. self.mode(chan=self.nickname, set=True, modes="B")
  639. self.sourceURLs=["https://github.com/TheLastProject/triggerbot", "https://notabug.org/SylvieLorxu/triggerbot"] # If you fork the bot, change this please
  640. if self.identify == True:
  641. self.msg('NickServ',
  642. 'IDENTIFY %s' % self.identifypassword)
  643. self.check_away_loop = task.LoopingCall(self.minutely_tasks)
  644. self.check_away_loop.start(60.0)
  645. self.daily_loop = task.LoopingCall(self.daily_tasks)
  646. self.daily_loop.start(86400.0)
  647. if not self.channelsdefined:
  648. self.channellist = self.get_settings().channels
  649. for channel in self.channellist:
  650. self.join_channel(channel)
  651. def minutely_tasks(self):
  652. self.claimNick()
  653. self.checkAway()
  654. self.save()
  655. def daily_tasks(self):
  656. self.purgeOldNicks()
  657. self.purgeOldLogs()
  658. self.purgeOldMessages()
  659. def joined(self, channel):
  660. """This will get called when the bot joins the channel."""
  661. self.logger.log("[I have joined %s]" % channel)
  662. channel = self.get_channel(channel)
  663. # Also join triggersafe channels
  664. userchannel = False
  665. try:
  666. if str(channel).split("_")[1]:
  667. userchannel = True
  668. channelowner = self.get_channel_owner(str(channel).split("_")[1])
  669. setattr(channel, "topicset", "%s's triggersafe channel. | %s[rules][mode]" % (channelowner, "[globalmotd]" if not self.check_for_master(self.get_user(channelowner)).motdread else ""))
  670. self.update_rules(channel=channel)
  671. except IndexError:
  672. setattr(channel, "topicset", "[globalmotd][rules]")
  673. if not userchannel:
  674. for user in self.users.itervalues():
  675. if user.channel:
  676. tojoin = "%s_%s" % (channel, user.nick)
  677. tojoin = tojoin.lower()
  678. self.join_channel(tojoin)
  679. def privmsg(self, user, channel, msg):
  680. """This will get called when the bot receives a message."""
  681. user = self.get_user(user.split('!', 1)[0])
  682. self.check_for_master(user).seen = datetime.datetime.now()
  683. checkmsg = msg.lstrip("!")
  684. # Do not log passwords
  685. if not checkmsg.startswith(("identify", "set password")):
  686. self.logger.log("[%s] <%s> %s" %
  687. (channel if is_channel_name(channel) else user, user, msg))
  688. # Private messages, messages beginning with "!" or messages directed to
  689. # me are commands.
  690. if is_channel_name(channel):
  691. channel = self.get_channel(channel)
  692. for prefix in ["!", self.nickname + ": ", self.nickname + ", "]:
  693. if msg.startswith(prefix):
  694. if user.ignored != True:
  695. self.dispatch(command=msg[len(prefix):], user=user, reply_to=channel)
  696. else:
  697. self.logger.log("Ignored command from %s" % user)
  698. return # Do not leak commands
  699. if "silent" in channel.mode:
  700. master = self.check_for_master(user)
  701. self.send_and_log(channel, user, "Your channel is currently in silent mode and therefore not relaying. Please unset silent mode by %s." % ("removing your away status" if (user.away and master.awaycheck and master.autosilence) else "executing '!mode remove silent'"))
  702. return
  703. if not "_" in channel.name and self.get_settings().maindisabled:
  704. if not user.channel:
  705. self.dispatch(command="set channel", user=user, reply_to=channel, bypass=True)
  706. self.send_and_log(channel, user, "The main channel has been disabled. Please join %s_%s to chat in this channel." % (channel, str(user).lower()))
  707. return
  708. self.relay_safe(message=msg, channel=channel, action=False, user=user, relateduser=None, chat=True)
  709. else:
  710. if not user.listenmode or checkmsg.startswith("unset listenmode"):
  711. self.dispatch(command=msg.lstrip("!"), user=user, reply_to=user)
  712. else:
  713. randomnumber = random.randrange(0,3)
  714. if randomnumber == 0:
  715. templates = [
  716. "nods calmly.",
  717. "nods.",
  718. ]
  719. message = random.choice(templates)
  720. reactor.callLater(random.randrange(2,5), self.describe, user, message)
  721. def action(self, user, channel, msg):
  722. """This will get called when the bot sees someone do an action."""
  723. user = self.get_user(user.split('!', 1)[0])
  724. self.check_for_master(user).seen = datetime.datetime.now()
  725. self.logger.log("[%s] * %s %s" %
  726. (channel if is_channel_name(channel) else user, user, msg))
  727. # Very important: Hug back when hugged
  728. try:
  729. if msg.split(' ')[0] == "hugs" and msg.split(' ')[1] == self.nickname:
  730. self.dispatch(command="hug", user=user, reply_to=channel if is_channel_name(channel) else user)
  731. except IndexError:
  732. # An ACTION with less than 2 words. Whatever it is, I am not being hugged.
  733. pass
  734. if is_channel_name(channel):
  735. channel = self.get_channel(channel)
  736. if "silent" in channel.mode:
  737. master = self.check_for_master(user)
  738. if user.away and master.awaycheck and master.autosilence:
  739. self.send_and_log(channel, user, "Your channel is currently in silent mode and therefore not relaying. Please unset silent mode by removing your away status.")
  740. else:
  741. self.send_and_log(channel, user, "Your channel is currently in silent mode and therefore not relaying. Please unset silent mode by executing '!mode remove silent'.")
  742. else:
  743. if not "_" in channel.name and self.get_settings().maindisabled:
  744. self.send_and_log(channel, user, "The main channel has been disabled. Please join %s_%s to chat in this channel." % (channel, str(user).lower()))
  745. if not user.channel:
  746. self.dispatch(command="set channel", user=user, reply_to=recipient, bypass=True)
  747. return
  748. self.relay_safe(message=msg, channel=channel, action=True, user=user, relateduser=None, chat=True)
  749. def notifyAdmins(self, channel, message):
  750. """ Notify the channel admins """
  751. for admin in channel.admins:
  752. admin = self.get_user(admin)
  753. self.send(admin, admin, message)
  754. def relay_safe(self, message, channel=None, action=False, user=None, relateduser=None, chat=True):
  755. if "silent" in channel.mode:
  756. return
  757. badtopics = []
  758. badwords = []
  759. triggeredusers = []
  760. ignoringusers = []
  761. collateralusers = []
  762. hiddenchannels = []
  763. hiddenusers = []
  764. try:
  765. basechannel = self.get_channel(str(channel).split("_")[0])
  766. except IndexError:
  767. basechannel = channel
  768. for triggersafechannel in self.channels:
  769. if str(triggersafechannel).startswith("%s_" % basechannel) and str(triggersafechannel) != str(channel):
  770. channelowner = str(triggersafechannel).split("_")[1]
  771. # Check if this channel has a normal user
  772. # If not, ignore it
  773. bots = [self.nickname, "ChanServ", "NickServ"]
  774. currentchannel = self.get_channel(triggersafechannel)
  775. channelusers = currentchannel.users
  776. for bot in bots:
  777. if bot in channelusers:
  778. channelusers.remove(bot)
  779. if not channelusers:
  780. hiddenchannels.append(currentchannel)
  781. continue
  782. # If we reached this point, someone is there. Keep it safe for them
  783. for checkinguser in channelusers:
  784. if currentchannel in hiddenchannels:
  785. if not checkinguser in collateralusers:
  786. collateralusers.append(checkinguser.nick)
  787. continue
  788. usertocheck = self.check_for_master(checkinguser)
  789. if "silent" in currentchannel.mode or ("rant" in channel.mode and (usertocheck.topics or usertocheck.trigger_words) and not "filterless" in currentchannel.mode) or user.nick in usertocheck.ignore:
  790. ignoringusers.append(usertocheck.nick)
  791. hiddenchannels.append(currentchannel)
  792. if not currentchannel in hiddenchannels and not "filterless" in currentchannel.mode:
  793. safe = self.is_safe(message=message, user=usertocheck)
  794. if not safe[0]:
  795. hiddenchannels.append(currentchannel)
  796. if checkinguser.away and usertocheck.awaycheck:
  797. if not usertocheck.nick in hiddenusers:
  798. hiddenusers.append(usertocheck.nick)
  799. else:
  800. for badtopic in safe[1]:
  801. if not badtopic in badtopics:
  802. badtopics.append(badtopic)
  803. for badword in safe[2]:
  804. if not badword in badwords:
  805. badwords.append(badword)
  806. if not usertocheck.nick in triggeredusers:
  807. triggeredusers.append(usertocheck.nick)
  808. for entry in ignoringusers:
  809. if not entry in hiddenusers:
  810. hiddenusers.append(entry.lower())
  811. if entry in collateralusers:
  812. collateralusers.remove(entry)
  813. for entry in triggeredusers:
  814. if not entry in hiddenusers:
  815. hiddenusers.append(entry.lower())
  816. if entry in collateralusers:
  817. collateralusers.remove(entry)
  818. if collateralusers:
  819. collateraldamage = " Due to user location, it was also hidden from %s." % join_and(", ", " and ", collateralusers)
  820. else:
  821. collateraldamage = ""
  822. # Tell users if stuff is bad and who we hid it from
  823. if badtopics and badwords:
  824. self.send_and_log(channel, user,
  825. "Because your message was possibly about the %s %s and contained the %s %s, it was hidden from %s.%s" %
  826. ("subjects" if len(badtopics) > 1 else "subject", join_and(", ", " and ", badtopics), \
  827. "words" if len(badwords) > 1 else "word", join_and(", ", " and ", badwords), \
  828. join_and(", ", " and ", triggeredusers), collateraldamage))
  829. user.warnings[datetime.datetime.now()] = (basechannel, None, "Said %r, containing %s %s and %s %s, being unsafe for %s." %
  830. (message, "subjects" if len(badtopics) > 1 else "subject", join_and(", ", " and ", badtopics), \
  831. "words" if len(badwords) > 1 else "word", join_and(", ", " and ", badwords), \
  832. join_and(", ", " and ", triggeredusers)))
  833. self.notifyAdmins(basechannel, "%s was prevented from triggering %s in %s. Type '!channel %s warnings list %s verbose 1' for more info." % (user, join_and(", ", " and ", triggeredusers), channel, basechannel, user))
  834. elif badtopics and not badwords:
  835. self.send_and_log(channel, user,
  836. "Because your message was possibly about the %s %s, it was hidden from %s.%s" %
  837. ("subjects" if len(badtopics) > 1 else "subject", join_and(", ", " and ", badtopics), \
  838. join_and(", ", " and ", triggeredusers), collateraldamage))
  839. user.warnings[datetime.datetime.now()] = (basechannel, None, "Said %r, containing %s %s, being unsafe for %s." %
  840. (message, "subjects" if len(badtopics) > 1 else "subject", join_and(", ", " and ", badtopics), \
  841. join_and(", ", " and ", triggeredusers)))
  842. self.notifyAdmins(basechannel, "%s was prevented from triggering %s in %s. Type '!channel %s warnings list %s verbose 1' for more info." % (user, join_and(", ", " and ", triggeredusers), channel, basechannel, user))
  843. elif badwords and not badtopics:
  844. self.send_and_log(channel, user,
  845. "Because your message contained the %s %s, it was hidden from %s.%s" %
  846. ("words" if len(badwords) > 1 else "word", join_and(", ", " and ", badwords), \
  847. join_and(", ", " and ", triggeredusers), collateraldamage))
  848. user.warnings[datetime.datetime.now()] = (basechannel, None, "Said %r, containing %s %s, being unsafe for %s." %
  849. (message, "words" if len(badwords) > 1 else "word", join_and(", ", " and ", badwords), \
  850. join_and(", ", " and ", triggeredusers)))
  851. self.notifyAdmins(basechannel, "%s was prevented from triggering %s in %s. Type '!channel %s warnings list %s verbose 1' for more info." % (user, join_and(", ", " and ", triggeredusers), channel, basechannel, user))
  852. self.relay(message=message, channel=channel, action=action, user=user, relateduser=relateduser, chat=chat, exclude=hiddenchannels)
  853. def is_safe(self, message, user):
  854. """ Check if a topic is message is safe for an user """
  855. badwords = []
  856. badtopics = []
  857. # First get a list of all words
  858. wordlist = []
  859. message = message.split(" ")
  860. for word in message:
  861. wordlist.append(word)
  862. for word in wordlist:
  863. stemmed = self.stem(word.lower())
  864. # Then check this user's trigger words:
  865. if stemmed in user.trigger_words:
  866. if not word in badwords:
  867. badwords.append(word)
  868. # Last but not least, check if the word is defined in a topic
  869. for topic in user.topics:
  870. level = user.topics[topic]
  871. for x in range(1, level+1):
  872. try:
  873. if stemmed in topic.words[x]:
  874. if not topic.name in badtopics:
  875. badtopics.append(topic.name)
  876. except KeyError:
  877. # No triggerwords defined for this level
  878. continue
  879. if badtopics or badwords:
  880. return [False, badtopics, badwords]
  881. else:
  882. return [True, None, None]
  883. def send(self, recipient, user, reply):
  884. if getattr(recipient, "is_user", False):
  885. self.msg(recipient.nick, reply)
  886. elif getattr(recipient, "is_channel", False):
  887. if user is not None:
  888. reply = "%s: %s" % (user.nick, reply)
  889. self.msg(recipient.name, reply)
  890. else:
  891. assert False
  892. def send_and_log(self, recipient, user, reply):
  893. if getattr(recipient, "is_user", False):
  894. self.msg(recipient.nick, reply)
  895. self.logger.log("[%s] <%s> %s" % (recipient.nick, self.nickname, reply))
  896. elif getattr(recipient, "is_channel", False):
  897. if user is not None:
  898. reply = "%s: %s" % (user.nick, reply)
  899. self.msg(recipient.name, reply)
  900. self.logger.log("[%s] <%s> %s" % (recipient.name, self.nickname, reply))
  901. else:
  902. assert False
  903. def relay(self, message, channel=None, action=False, user=None, relateduser=None, chat=True, exclude=[], globalrelay=False, notifyfriends=False):
  904. """This will relay something to all related channels."""
  905. if channel == None:
  906. for channel in self.channels.itervalues():
  907. try:
  908. if str(channel).split("_")[1]:
  909. pass
  910. except IndexError:
  911. self.relay(message=message, channel=channel, action=action, user=user, relateduser=relateduser, chat=chat, exclude=exclude, globalrelay=True, notifyfriends=notifyfriends)
  912. else:
  913. excluded = []
  914. for excluding in exclude:
  915. excluded.append(excluding)
  916. if user != None:
  917. for checkignore in self.check_for_master(user).ignoredby:
  918. if not checkignore in excluded:
  919. excluded.append(checkignore)
  920. if relateduser != None:
  921. for checkignore in self.check_for_master(relateduser).ignoredby:
  922. if not checkignore in excluded:
  923. excluded.append(checkignore)
  924. if str(channel).split("_")[0]:
  925. basechannel = Channel(str(channel).split("_")[0])
  926. for relaychannel in self.channels.itervalues():
  927. if str(relaychannel).startswith("%s_" % basechannel) and str(relaychannel) != str(channel) and not relaychannel in excluded:
  928. messagetosend = message
  929. if notifyfriends:
  930. friends = []
  931. for possiblefriend in relaychannel.users:
  932. if relateduser.nick in possiblefriend.friends:
  933. friends.append(possiblefriend)
  934. if friends:
  935. messagetosend = "%s (This is a friend of you, %s)" % (message, join_and(", ", " and ", friends))
  936. if action:
  937. if chat:
  938. self.send(relaychannel, None,
  939. "* %s %s" % (user.nick, messagetosend))
  940. else:
  941. self.send(relaychannel, None,
  942. "[INFO] * %s %s" % (user.nick, messagetosend))
  943. else:
  944. if user:
  945. if chat:
  946. self.send(relaychannel, None,
  947. "<%s> %s" % (user.nick, messagetosend))
  948. else:
  949. self.send(relaychannel, None,
  950. "[INFO] <%s> %s" % (user.nick, messagetosend))
  951. else:
  952. if chat:
  953. self.send(relaychannel, None,
  954. messagetosend)
  955. else:
  956. self.send(relaychannel, None,
  957. "[INFO] %s" % messagetosend)
  958. if (str(basechannel) != str(channel) or (globalrelay and not channel in excluded)) and is_channel_name(str(channel)):
  959. if self.get_settings().maindisabled:
  960. # If the main channel is disabled, don't relay to there.
  961. return
  962. messagetosend = message
  963. if notifyfriends:
  964. friends = []
  965. for possiblefriend in relaychannel.users:
  966. if relateduser.nick in possiblefriend.friends:
  967. friends.append(possiblefriend)
  968. if friends:
  969. messagetosend = "%s (This is a friend %s)" % (message, join_and(", ", " and ", friends))
  970. if action:
  971. if chat:
  972. self.send(basechannel, None,
  973. "* %s %s" % (user.nick, messagetosend))
  974. else:
  975. self.send(basechannel, None,
  976. "[INFO] * %s %s" % (user.nick, messagetosend))
  977. else:
  978. if user:
  979. if chat:
  980. self.send(basechannel, None,
  981. "<%s> %s" % (user.nick, messagetosend))
  982. else:
  983. self.send(basechannel, None,
  984. "[INFO] <%s> %s" % (user.nick, messagetosend))
  985. else:
  986. if chat:
  987. self.send(basechannel, None,
  988. messagetosend)
  989. else:
  990. self.send(basechannel, None,
  991. "[INFO] %s" % messagetosend)
  992. def claimNick(self):
  993. """ This makes sure the bot will eventually get the nickname it wants. """
  994. if self.nickname != self.wantednick:
  995. if self.identify:
  996. self.msg('NickServ',
  997. 'GHOST %s %s' % (self.wantednick, self.identifypassword))
  998. self.setNick(self.wantednick)
  999. if self.identify:
  1000. self.msg('NickServ',
  1001. 'IDENTIFY %s' % self.identifypassword)
  1002. def checkAway(self, user=None):
  1003. """This checks if someone is away by calling WHO."""
  1004. if user is None:
  1005. # Check all users!
  1006. self.sendLine("WHO 0")
  1007. else:
  1008. # Check a single user.
  1009. self.sendLine("WHO %s" % user)
  1010. def purgeOldNicks(self):
  1011. """ This gets rid of user entries in the database for nicknames
  1012. which haven't been used in the last 30 days """
  1013. for user in self.users.values()[:]:
  1014. # Don't purge admins or accounts explicitly marked as do-not-purge
  1015. if not user.admin and user.autopurge and (datetime.datetime.now() - user.lastlogout).days > 30:
  1016. useronline = False
  1017. for channel in self.channels:
  1018. if user in self.get_channel(channel).users:
  1019. useronline = True
  1020. if not useronline:
  1021. if user.channel:
  1022. self.dispatch(command='unset channel', user=user, reply_to=user, bypass=True)
  1023. del self.users[user.nick]
  1024. def purgeOldLogs(self):
  1025. """ This gets rid of admin logs older than 30 days """
  1026. for user in self.users:
  1027. user = self.get_user(user)
  1028. for logdate in user.logs.keys():
  1029. if (datetime.datetime.now() - logdate).days > 30:
  1030. del user.logs[logdate]
  1031. self.__dirty = True
  1032. def purgeOldMessages(self):
  1033. """ This gets rid of user messages older than the defined limit """
  1034. for user in self.users:
  1035. user = self.get_user(user)
  1036. for messagedate in user.messages.keys():
  1037. if (datetime.datetime.now() - messagedate).days > user.messagestoretime:
  1038. del user.messages[messagedate]
  1039. if messagedate in user.readmessages:
  1040. user.readmessages.remove(messagedate)
  1041. self.__dirty = True
  1042. def irc_RPL_WHOREPLY(self, prefix, params):
  1043. (my_nick, channel, username, hostmask, server,
  1044. nickname, flags, hops_and_realname) = params
  1045. hops, realname = hops_and_realname.split(" ", 1)
  1046. user = self.get_user(nickname)
  1047. master = self.check_for_master(user)
  1048. user.host = hostmask
  1049. # Check identified status
  1050. if "r" in flags and master.nickservlogin:
  1051. user.logged_in = True
  1052. # Check away status
  1053. if "H" in flags:
  1054. user.away = False
  1055. if master.channel and master.autosilence:
  1056. for channel in self.channels:
  1057. if not channel.endswith("_%s" % str(master).lower()):
  1058. continue
  1059. channel = self.get_channel(channel)
  1060. if "silent" in channel.mode:
  1061. self.dispatch(command="mode remove silent auto", user=user, reply_to=channel, bypass=True)
  1062. elif "G" in flags:
  1063. if not master.awaycheck:
  1064. return
  1065. user.away = True
  1066. if master.channel and master.topics and master.autosilence:
  1067. for channel in self.channels:
  1068. if not channel.endswith("_%s" % str(master).lower()):
  1069. continue
  1070. channel = self.get_channel(channel)
  1071. # Only silence a channel if all its users are away.
  1072. allaway = True
  1073. for usercheck in channel.users:
  1074. if usercheck.nick in ["ChanServ", "NickServ", self.nickname]:
  1075. continue
  1076. if not usercheck.away:
  1077. allaway = False
  1078. break
  1079. if allaway and not "silent" in channel.mode:
  1080. self.dispatch(command="mode add silent auto", user=user, reply_to=channel, bypass=True)
  1081. def irc_RPL_ENDOFWHO(self, prefix, params):
  1082. self.update_rules()
  1083. def irc_RPL_TOPIC(self, prefix, params):
  1084. self.irc_TOPIC(prefix, params)
  1085. def irc_TOPIC(self, prefix, params):
  1086. if len(params) > 2: # Yay, dealing with irc_TOPIC and irc_RPL_TOPIC inconsistencies
  1087. channel = self.get_channel(params[1])
  1088. topic = params[2]
  1089. changer = params[0]
  1090. else:
  1091. channel = self.get_channel(params[0])
  1092. topic = params[1]
  1093. changer = prefix.split("!")[0]
  1094. if changer == self.nickname:
  1095. setattr(channel, "topic", topic)
  1096. else:
  1097. setattr(channel, "topicset", topic)
  1098. def userRenamed(self, oldnick, newnick):
  1099. self.logger.log("%s is now known as %s." % (oldnick, newnick))
  1100. olduser = self.get_user(oldnick)
  1101. newuser = self.get_user(newnick)
  1102. self.check_for_master(olduser).seen = datetime.datetime.now()
  1103. self.check_for_master(newuser).seen = datetime.datetime.now()
  1104. relayedchannels = []
  1105. for channel in self.channels.itervalues():
  1106. if olduser in channel.users:
  1107. channel.users.remove(olduser)
  1108. channel.users.add(newuser)
  1109. if not str(channel).split("_")[0] in relayedchannels:
  1110. self.relay("%s is now known as %s." % # TODO: Fake join/quit when old/new nick is on ignore list
  1111. (oldnick, newnick), channel, chat=False)
  1112. relayedchannels.append(str(channel).split("_")[0])
  1113. # This code is used to get a list of people there.
  1114. def irc_RPL_NAMREPLY(self, prefix, params):
  1115. "Receive NAMES reply from server"
  1116. my_user, _, channel, nicks = params
  1117. channel = self.get_channel(channel)
  1118. for nick in nicks.split():
  1119. nick = re.sub(r'^[~&@%+]', "", nick)
  1120. if nick != self.nickname:
  1121. user = self.get_user(nick)
  1122. channel.users.add(user)
  1123. def irc_RPL_ENDOFNAMES(self, prefix, params):
  1124. "We know everyone. List admins and rules"
  1125. my_user, channel, _ = params
  1126. channel = self.get_channel(channel)
  1127. self.logger.log("[%s] NAMES: %s" % (channel.name,
  1128. " ".join(user.nick for user in channel.users)))
  1129. reactor.callLater(10, self.update_rules, channel=channel)
  1130. def userJoined(self, nick, channel):
  1131. self.logger.log("[%s] %s has joined." % (channel, nick))
  1132. user = self.get_user(nick, create_if_nonexistent=False)
  1133. joinchannel = self.get_channel(channel)
  1134. users = []
  1135. for onlineuser in self.get_channel(str(channel).split("_")[0]).users:
  1136. users.append(onlineuser.nick)
  1137. for relaychannel in self.channels.itervalues():
  1138. try:
  1139. if str(relaychannel).split("_")[1]:
  1140. if str(relaychannel).split("_")[0] == str(channel).split("_")[0]:
  1141. for onlineuser in relaychannel.users:
  1142. if not onlineuser.nick in users:
  1143. users.append(onlineuser.nick)
  1144. except IndexError:
  1145. pass
  1146. if user == None:
  1147. user = self.get_user(nick)
  1148. self.send_and_log(joinchannel, user,
  1149. "Hello, %s! I haven't seen you before. If you want, you can take a tutorial on how to best use %s by typing '!tutorial'."
  1150. % (user, self.nickname))
  1151. elif user.nick in ["NickServ", "ChanServ"]:
  1152. # This ensures that we'll identify to services after they recover from a crash, assuming ChanServ joins our channel
  1153. if self.identify == True:
  1154. self.msg('NickServ',
  1155. 'IDENTIFY %s' % self.identifypassword)
  1156. elif len(user.messages) > len(user.readmessages):
  1157. self.send_and_log(joinchannel, user,
  1158. "You have unread messages. Please check them using '!mail inbox unread'.")
  1159. if (not "_" in channel and not self.get_settings().maindisabled) or ("_" in channel and not "silent" in joinchannel.mode):
  1160. if not nick in users:
  1161. self.relay("%s has joined." % nick, joinchannel, relateduser=self.find_user(nick), chat=False, notifyfriends=True)
  1162. joinchannel.users.add(user)
  1163. elif self.get_settings().maindisabled:
  1164. if not user.channel:
  1165. self.dispatch(command="set channel", user=user, reply_to=joinchannel, bypass=True)
  1166. self.send_and_log(joinchannel, user, "The main channel has been disabled. Please join %s_%s to chat in this channel." % (joinchannel, str(user).lower()))
  1167. if "_" in channel and user.nick not in ["NickServ", "ChanServ"]:
  1168. self.dispatch(command="names", user=None, reply_to=joinchannel)
  1169. self.check_for_master(user).seen = datetime.datetime.now()
  1170. self.checkAway(user=user)
  1171. def userLeft(self, nick, channel):
  1172. self.logger.log("[%s] %s has left." % (channel, nick))
  1173. user = self.get_user(nick)
  1174. master = self.check_for_master(user)
  1175. leavechannel = self.get_channel(channel)
  1176. try:
  1177. leavechannel.users.remove(user)
  1178. except KeyError:
  1179. return
  1180. stillonline = False
  1181. hideleave = False
  1182. for channel in self.channels.itervalues():
  1183. if user in channel.users:
  1184. if not "silent" in channel.mode:
  1185. stillonline = True
  1186. else:
  1187. hideleave = True
  1188. if not stillonline:
  1189. if not hideleave:
  1190. self.relay("%s has left." % nick, leavechannel, relateduser=user, chat=False)
  1191. if master.autologout:
  1192. user.logged_in = False
  1193. self.update_rules(channel=channel)
  1194. master.seen = datetime.datetime.now()
  1195. user.lastlogout = datetime.datetime.now()
  1196. def userQuit(self, nick, message):
  1197. self.logger.log("%s has quit (%s)." % (nick, message))
  1198. user = self.get_user(nick)
  1199. exclude = []
  1200. for channel in self.channels.itervalues():
  1201. channel.users.discard(user)
  1202. if user in channel.users or ("_" in channel and "silent" in channel.mode):
  1203. exclude.append(channel)
  1204. if not message.startswith("Quit: "):
  1205. self.relay("%s has quit (%s)." % (nick, message), relateduser=user, chat=False, exclude=exclude)
  1206. else:
  1207. self.relay("%s has quit." % nick, relateduser=user, chat=False, exclude=exclude)
  1208. master = self.check_for_master(user)
  1209. master.seen = datetime.datetime.now()
  1210. user.lastlogout = datetime.datetime.now()
  1211. if master.autologout:
  1212. user.logged_in = False
  1213. def userKicked(self, kicked, channel, kicker, message):
  1214. try:
  1215. oldmessage = message
  1216. message = message.split(": ")[1]
  1217. kicker = oldmessage.split(": ")[0]
  1218. except IndexError:
  1219. pass
  1220. self.logger.log("[%s] %s was kicked by %s (%s)." %
  1221. (channel, kicked, kicker, message))
  1222. channel = self.get_channel(channel)
  1223. try:
  1224. channel.users.remove(self.get_user(kicked))
  1225. except KeyError:
  1226. return
  1227. users = []
  1228. for onlineuser in self.get_channel(str(channel).split("_")[0]).users:
  1229. users.append(onlineuser.nick)
  1230. for relaychannel in self.channels.itervalues():
  1231. try:
  1232. if str(relaychannel).split("_")[1]:
  1233. if str(relaychannel).split("_")[0] == str(channel).split("_")[0] and relaychannel != channel:
  1234. for onlineuser in relaychannel.users:
  1235. if not onlineuser.nick in users:
  1236. users.append(onlineuser.nick)
  1237. except IndexError:
  1238. pass
  1239. if not kicked in users:
  1240. self.relay("%s has left (kicked by %s)." %
  1241. (kicked, kicker), channel, relateduser=self.find_user(kicked), chat=False)
  1242. else:
  1243. self.relay("%s was kicked by %s." %
  1244. (kicked, kicker), channel, relateduser=self.find_user(kicked), chat=False)
  1245. self.update_rules(channel=channel)
  1246. user = self.get_user(kicked)
  1247. self.check_for_master(user).seen = datetime.datetime.now()
  1248. user.lastlogout = datetime.datetime.now()
  1249. def irc_RPL_AWAY(self, prefix, params):
  1250. nick = params[1]
  1251. self.get_user(nick).away = True
  1252. self.update_rules()
  1253. def admin_command(f):
  1254. def wrapper(bot, params, user, recipient, mainchannel, bypass=False):
  1255. master = bot.check_for_master(user)
  1256. # This 'commandcores' silliness is to allow users to execute all subcommands of allowed commands
  1257. commandcores = []
  1258. for x in range(1, len(wrapper.__name__.split("_"))+1):
  1259. commandcores.append("_".join(wrapper.__name__.split("_")[:x]))
  1260. if not master.admin and not any(commandcore in master.admincommandsallowed for commandcore in commandcores):
  1261. bot.send_and_log(recipient, user,
  1262. "You are not authorized to use this command.")
  1263. else:
  1264. return f(bot, params, user, recipient, mainchannel, bypass)
  1265. wrapper.__name__ = f.__name__
  1266. return wrapper
  1267. def channelmod_command(f):
  1268. def wrapper(bot, params, user, recipient, mainchannel, bypass=False):
  1269. master = bot.check_for_master(user)
  1270. if not is_channel_name(str(mainchannel)):
  1271. bot.send_and_log(recipient, user,
  1272. "This command needs to be executed in-channel, or given the channel name as first parameter. Please run it in either a main or triggersafe channel, or give the channel name.")
  1273. elif not master.nick in mainchannel.admins and not master.admin:
  1274. bot.send_and_log(recipient, user,
  1275. "You are not authorized to use this command.")
  1276. else:
  1277. return f(bot, params, user, recipient, mainchannel, bypass)
  1278. wrapper.__name__ = f.__name__
  1279. return wrapper
  1280. def toggleable_command(f):
  1281. def wrapper(bot, params, user, recipient, mainchannel, bypass=False):
  1282. commandcores = []
  1283. for x in range(1, len(wrapper.__name__.split("_"))+1):
  1284. commandcores.append("_".join(wrapper.__name__.split("_")[:x]))
  1285. if any(commandcore in bot.get_settings().disabledcommands for commandcore in commandcores):
  1286. bot.send_and_log(recipient, user,
  1287. "This command has been disabled by an administrator.")
  1288. else:
  1289. return f(bot, params, user, recipient, mainchannel, bypass)
  1290. wrapper.__name__ = f.__name__
  1291. return wrapper
  1292. def protected_command(f):
  1293. def wrapper(bot, params, user, recipient, mainchannel, bypass=False):
  1294. master = bot.check_for_master(user)
  1295. if not bypass and master.password != None and user.logged_in != True:
  1296. raise AuthenticationError
  1297. else:
  1298. return f(bot, params, user, recipient, mainchannel, bypass)
  1299. wrapper.__name__ = f.__name__
  1300. return wrapper
  1301. def logged_command(f):
  1302. def wrapper(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1303. user = bot.check_for_master(user_executed)
  1304. user.logs[datetime.datetime.now()] = [mainchannel, "%s %s" % (" ".join(wrapper.__name__.split("_")), " ".join(params))]
  1305. return f(bot, params, user_executed, recipient, mainchannel, bypass)
  1306. wrapper.__name__ = f.__name__
  1307. return wrapper
  1308. def register_commands():
  1309. """All the commands are defined in here"""
  1310. command = TriggerBot.add_command
  1311. @command("Lists the bot admins that are currently online.\n"
  1312. "admins")
  1313. @toggleable_command
  1314. def admins(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1315. avail_admins = [user.nick for channel in bot.channels.itervalues()
  1316. for user in channel.users
  1317. if not user.away and (user.admin or (user.nick in mainchannel.admins and str(mainchannel) == str(channel).split("_")[0]))]
  1318. unavail_admins = [user.nick for user in bot.users.itervalues()
  1319. if (user.admin or user.nick in mainchannel.admins) and user.nick not in avail_admins]
  1320. if avail_admins:
  1321. bot.send_and_log(recipient, user_executed,
  1322. "The following people are currently available: "
  1323. + ", ".join(["%s (%s)" % (user.nick, "head admin" if user.admin == 1 else ("admin" if user.admin else "channel admin")) for user in bot.users.itervalues() if user.nick in avail_admins]))
  1324. else:
  1325. bot.send_and_log(recipient, user_executed,
  1326. "Nobody is currently available.")
  1327. if unavail_admins:
  1328. bot.send_and_log(recipient, user_executed,
  1329. "The following people are currently unavailable: "
  1330. + ", ".join(["%s (%s)" % (user.nick, "head admin" if user.admin == 1 else ("admin" if user.admin else "channel admin")) for user in bot.users.itervalues() if user.nick in unavail_admins]))
  1331. @command("Anonymously request a topic change.\n"
  1332. "change [<channel>]")
  1333. @toggleable_command
  1334. def change(bot, params, user, recipient, mainchannel, bypass=False):
  1335. if len(params) > 0:
  1336. channel = bot.get_channel(params[0])
  1337. if not is_channel_name(channel.name):
  1338. channel.name = "#%s" % channel.name
  1339. else:
  1340. channel = recipient
  1341. if not getattr(channel, "is_channel", False):
  1342. bot.send_and_log(recipient, user,
  1343. "Please specify a channel to request a topic change for.")
  1344. return
  1345. bot.relay_safe(message="Someone is feeling uncomfortable with this disussion. Could we talk about something else?", channel=channel)
  1346. bot.notifyAdmins(mainchannel, "%s issued !change for %s. Please ensure everyone is sticking to the rules." % (user, channel))
  1347. # TODO: Add channel mod commands here
  1348. @command("Manage a channel.\n"
  1349. "channel")
  1350. def channel(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1351. raise BadCommand
  1352. @command("Add one or more users as channel administrator (admin).\n"
  1353. "channel add <user>")
  1354. @channelmod_command
  1355. @protected_command
  1356. @logged_command
  1357. def channel_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1358. if params:
  1359. for nick in params:
  1360. nick = bot.find_user(nick)
  1361. master = bot.check_for_master(nick)
  1362. if master.nick not in mainchannel.admins:
  1363. mainchannel.admins.append(master.nick)
  1364. bot.send_and_log(recipient, user_executed,
  1365. "Requested user(s) now have channel administrator status.")
  1366. bot.changed()
  1367. else:
  1368. raise MissingParams
  1369. @command("Remove the channel administrator status from one or more users.\n"
  1370. "channel remove <user>")
  1371. @channelmod_command
  1372. @protected_command
  1373. @logged_command
  1374. def channel_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1375. if params:
  1376. for nick in params:
  1377. if nick in mainchannel.admins:
  1378. mainchannel.admins.remove(nick)
  1379. bot.send_and_log(recipient, user_executed,
  1380. "Requested user(s) no longer have channel administrator status.")
  1381. bot.changed()
  1382. else:
  1383. raise MissingParams
  1384. @command("Sent an announcement to all users in the channel.\n"
  1385. "channel announce <message>")
  1386. @channelmod_command
  1387. @protected_command
  1388. @logged_command
  1389. def channel_announce(bot, params, user, recipient, mainchannel, bypass=False):
  1390. if params:
  1391. for channel in bot.channels.itervalues():
  1392. if str(channel) == str(mainchannel) or str(channel).startswith("%s_" % mainchannel):
  1393. bot.notice(channel,
  1394. "Announcement from %s: %s" %
  1395. (user.nick, " ".join(params)))
  1396. else:
  1397. raise MissingParams
  1398. @command("Order the bot to kick someone out.\n"
  1399. "channel kick <user> [<reason>]")
  1400. @channelmod_command
  1401. @protected_command
  1402. @logged_command
  1403. def channel_kick(bot, params, user, recipient, mainchannel, bypass=False):
  1404. if not params:
  1405. raise MissingParams
  1406. for channel in bot.channels.itervalues():
  1407. if str(channel) == str(mainchannel) or str(channel).startswith("%s_" % mainchannel):
  1408. bot.kick(str(channel), params[0], "%s: %s" % (user, params[1:] if len(params) >= 2 else "no reason specified."))
  1409. @command("Order the bot to ban an user.\n"
  1410. "channel ban <user>")
  1411. @channelmod_command
  1412. @protected_command
  1413. @logged_command
  1414. def channel_ban(bot, params, user, recipient, mainchannel, bypass=False):
  1415. if not params:
  1416. raise MissingParams
  1417. toban = bot.get_user(params[0])
  1418. for channel in bot.channels.itervalues():
  1419. if str(channel) == str(mainchannel) or str(channel).startswith("%s_" % mainchannel):
  1420. bot.mode(chan=str(channel), set=True, modes="b", mask="*!*@%s" % toban.host)
  1421. @command("Remove the ban on an user.\n"
  1422. "channel unban <user>")
  1423. @channelmod_command
  1424. @protected_command
  1425. @logged_command
  1426. def channel_unban(bot, params, user, recipient, mainchannel, bypass=False):
  1427. tounban = bot.get_user(params[0])
  1428. for channel in bot.channels.itervalues():
  1429. if str(channel) == str(mainchannel) or str(channel).startswith("%s_" % mainchannel):
  1430. bot.mode(chan=str(channel), set=False, modes="b", mask="*!*@%s" % tounban.host)
  1431. @command("Order the bot to kickban an user.\n"
  1432. "channel kickban <user>")
  1433. @channelmod_command
  1434. @protected_command
  1435. @logged_command
  1436. def channel_kickban(bot, params, user, recipient, mainchannel, bypass=False):
  1437. bot.dispatch(command="channel kick %s" % ' '.join(params), user=user, reply_to=recipient, bypass=True)
  1438. bot.dispatch(command="channel ban %s" % ' '.join(params), user=user, reply_to=recipient, bypass=True)
  1439. @command("View channel logs.\n"
  1440. "By default, the 10 latest entries are displayed. Type a number or 'all' to see more entries.\n"
  1441. "channel logs [<user>] [<entries>]")
  1442. @channelmod_command
  1443. @protected_command
  1444. def channel_logs(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1445. start = -10
  1446. tosend = []
  1447. timelength = 4
  1448. userlength = 4
  1449. users = []
  1450. if params:
  1451. for param in params:
  1452. try:
  1453. users.append(bot.find_user(param))
  1454. except UserNotFound:
  1455. if param == "all":
  1456. start = 0
  1457. else:
  1458. try:
  1459. start = -int(param)
  1460. except ValueError:
  1461. raise UserNotFound(param)
  1462. if not users:
  1463. users = bot.users.itervalues()
  1464. logs = {}
  1465. for user in users:
  1466. for log in user.logs.keys():
  1467. if user.logs[log][0] != mainchannel:
  1468. # Not related to this channel. Ignore
  1469. continue
  1470. logs[log] = user.nick, user.logs[log]
  1471. if not logs:
  1472. bot.send_and_log(recipient, user_executed,
  1473. "No logs were found for %s" % join_and(", ", " and ", users))
  1474. return
  1475. if start < 0 and -start < len(logs):
  1476. bot.send_and_log(recipient, user_executed,
  1477. "Limiting output to the most recent %s of %s entries." % (-start, len(logs)))
  1478. else:
  1479. bot.send_and_log(recipient, user_executed,
  1480. "Showing all %s entries." % len(logs))
  1481. sorted_logs = sorted(logs.keys())[start:]
  1482. for log in sorted_logs:
  1483. # Time | User | Command
  1484. time = log.strftime("%Y-%m-%d %H:%M:%S")
  1485. user = logs[log][0]
  1486. command = logs[log][1][1]
  1487. tosend.append("%s | %s | %s" % (time, user, command))
  1488. if len(time) > timelength:
  1489. timelength = len(time)
  1490. if len(user) > userlength:
  1491. userlength = len(user)
  1492. bot.send_and_log(recipient, user_executed,
  1493. "%s | %s | command" % ("time".center(timelength), "user".center(userlength)))
  1494. for abouttosend in tosend:
  1495. abouttosend = "%s | %s | %s" % (abouttosend.split(" | ")[0].center(timelength), abouttosend.split(" | ")[1].center(userlength), abouttosend.split(" | ")[2])
  1496. bot.send_and_log(recipient, user_executed, abouttosend)
  1497. @command("Manage trigger topic blocks in channel.\n"
  1498. "channel topicblock")
  1499. def channel_topicblock(bot, params, user, recipient, mainchannel, bypass=False):
  1500. raise BadCommand
  1501. @command("Block users with a certain trigger topic set.\n"
  1502. "channel topicblock add <topic> <level>")
  1503. @channelmod_command
  1504. @protected_command
  1505. @logged_command
  1506. def channel_topicblock_add(bot, params, user, recipient, mainchannel, bypass=False):
  1507. if len(params) % 2:
  1508. raise WrongParamCount
  1509. number = 0
  1510. while number < len(params)-1:
  1511. try:
  1512. if not int(params[number+1]) in bot.find_topic(params[number].lower()).descriptions.keys():
  1513. bot.send_and_log(recipient, user, "Topic %r does not have a level %r" % (params[number], params[number+1]))
  1514. return
  1515. except ValueError:
  1516. bot.send_and_log(recipient, user, "Please enter a number for topic level")
  1517. return
  1518. mainchannel.blockedtopics[params[number]] = int(params[number+1])
  1519. number += 2
  1520. bot.send_and_log(recipient, user,
  1521. "The requested topics are now blocked")
  1522. bot.changed()
  1523. @command("Unblock one or more trigger topics.\n"
  1524. "channel topicblock remove <topic>")
  1525. @channelmod_command
  1526. @protected_command
  1527. @logged_command
  1528. def channel_topicblock_remove(bot, params, user, recipient, mainchannel, bypass=False):
  1529. for param in params:
  1530. del mainchannel.blockedtopics[param]
  1531. bot.send_and_log(recipient, user,
  1532. "The requested topics are no longer blocked")
  1533. bot.changed()
  1534. @command("List blocked trigger topics.\n"
  1535. "channel topicblock list")
  1536. @channelmod_command
  1537. @protected_command
  1538. @logged_command
  1539. def channel_topicblock_list(bot, params, user, recipient, mainchannel, bypass=False):
  1540. # FIXME: Format this nicely
  1541. bot.send_and_log(recipient, user, mainchannel.blockedtopics)
  1542. @command("Warn an user.\n"
  1543. "channel warn <user> [<reason>]")
  1544. @channelmod_command
  1545. @protected_command
  1546. @logged_command
  1547. def channel_warn(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1548. if len(params) > 0:
  1549. user = bot.find_user(params[0])
  1550. user.warnings[datetime.datetime.now()] = mainchannel, user_executed.nick, str(" ".join(params[1:])) if len(params) > 1 else None
  1551. warningsbytriggerbot = 0
  1552. for entry in user.warnings.keys():
  1553. if user.warnings[entry][1] == None:
  1554. warningsbytriggerbot = warningsbytriggerbot + 1
  1555. if user.warnings[entry][2:] == None:
  1556. bot.send_and_log(user, None,
  1557. "%s has sent you a warning."
  1558. % user.warnings[entry][1])
  1559. else:
  1560. bot.send_and_log(user, None,
  1561. "%s has sent you a warning with the following reason: %s."
  1562. % (user.warnings[entry][1], user.warnings[entry][2]))
  1563. bot.send_and_log(user, None,
  1564. "Please think about your behaviour and try to improve it.")
  1565. bot.send_and_log(user, None,
  1566. "For your information, you currently have %s warning(s), of which %s were automatically sent to you by %s." %
  1567. (len(user.warnings), warningsbytriggerbot, bot.nickname))
  1568. else:
  1569. raise MissingParams
  1570. @command("Manage an user's warnings.")
  1571. @channelmod_command
  1572. @protected_command
  1573. def channel_warnings(bot, params, user, recipient, mainchannel, bypass=False):
  1574. raise BadCommand
  1575. @command("List the amount of warnings an user has in this channel.\n"
  1576. "Use 'verbose' to receive a more verbose list.\n"
  1577. "By default, only the last 10 entries are shown in verbose mode. Type a number on 'all' to show more entries.\n"
  1578. "channel warnings list <user> [<type>] [<entries>]")
  1579. @channelmod_command
  1580. @protected_command
  1581. def channel_warnings_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1582. if len(params) > 0:
  1583. user = bot.find_user(params[0])
  1584. warnings = {}
  1585. for entry in user.warnings.keys():
  1586. if user.warnings[entry][0] == mainchannel:
  1587. warnings[entry] = user.warnings[entry]
  1588. if len(warnings) > 0:
  1589. if len(params) <= 1 or params[1] != "verbose":
  1590. warningsbyuser = {}
  1591. for entry in warnings.keys():
  1592. warner = warnings[entry][1] if warnings[entry][1] != None else bot.nickname
  1593. if warner in warningsbyuser:
  1594. warningsbyuser[warner] = warningsbyuser[warner] + 1
  1595. else:
  1596. warningsbyuser[warner] = 1
  1597. bot.send_and_log(recipient, user_executed,
  1598. "Since %s, %s has received %s warning(s) in this channel (%s total). %s." %
  1599. (warnings.keys()[0].strftime("%Y-%m-%d %H:%M:%S"), user.nick, len(warnings.keys()), len(user.warnings.keys()), ', '.join(["%s by %s" %
  1600. (warningdata[1], warningdata[0]) for warningdata in warningsbyuser.items()])))
  1601. else:
  1602. if user.warnings:
  1603. start = len(warnings)-10
  1604. try:
  1605. if params[2] == "all":
  1606. start = 0
  1607. else:
  1608. try:
  1609. start = len(warnings)-int(params[2])
  1610. except:
  1611. pass
  1612. except:
  1613. pass
  1614. if start > 0:
  1615. bot.send_and_log(recipient, user_executed,
  1616. "Limiting output to the most recent %s of %s entries." % (len(warnings)-start, len(warnings)))
  1617. else:
  1618. bot.send_and_log(recipient, user_executed,
  1619. "Showing all %s entries." % len(warnings))
  1620. for number, warning in enumerate(warnings.keys()[start:]):
  1621. bot.send_and_log(recipient, user_executed,
  1622. "%s - warned by %s: %s"
  1623. % (warnings.keys()[number].strftime("%Y-%m-%d %H:%M:%S"), bot.nickname if warnings[warning][1] == None else warnings[warning][1], "No reason specified." if warnings[warning][2] == None else warnings[warning][2]))
  1624. else:
  1625. bot.send_and_log(recipient, user_executed,
  1626. "%s has not received any warnings%s." % (user, " in this channel" if user.warnings else ""))
  1627. else:
  1628. raise MissingParams
  1629. @command("Reset one or more users' warnings in this channel.\n"
  1630. "channel warnings reset <user>")
  1631. @channelmod_command
  1632. @protected_command
  1633. @logged_command
  1634. def channel_warnings_reset(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1635. if len(params) > 0:
  1636. for nick in params:
  1637. user = bot.find_user(nick)
  1638. warnings = {}
  1639. for entry in user.warnings.keys():
  1640. if user.warnings[entry][0] != mainchannel:
  1641. warnings[entry] = user.warnings[entry]
  1642. user.warnings = warnings
  1643. bot.send_and_log(recipient, user_executed,
  1644. "Warnings reset.")
  1645. else:
  1646. raise MissingParams
  1647. # TODO: End channel mod commands here
  1648. @command("Manage nicks allowed in your channel.\n"
  1649. "Nicks on this list will not be kicked from your channel, even if they are not in your alt list.")
  1650. @toggleable_command
  1651. def channelallow(bot, params, user, recipient, mainchannel, bypass=False):
  1652. raise BadCommand
  1653. @command("Add one or more user(s) to your channelallow list."
  1654. "channelallow add <user>")
  1655. @protected_command
  1656. @toggleable_command
  1657. def channelallow_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1658. if params:
  1659. user = bot.check_for_master(user_executed)
  1660. for entry in params:
  1661. if entry not in user.channelallow and entry != user_executed.nick and entry != user.nick and entry not in user.alts:
  1662. user.channelallow.append(entry)
  1663. for channel in bot.channels.itervalues():
  1664. if str(channel).endswith("_%s" % user.nick):
  1665. self.mode(channel, True, "I %s!*@*" % allowed) # Add an invite-only exception
  1666. bot.send_and_log(recipient, user_executed,
  1667. "The requested users were added to your channelallow list.")
  1668. bot.changed()
  1669. else:
  1670. raise MissingParams
  1671. @command("Remove one or more user(s) from your channelallow list."
  1672. "channelallow remove <user>")
  1673. @protected_command
  1674. @toggleable_command
  1675. def channelallow_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1676. if params:
  1677. user = bot.check_for_master(user_executed)
  1678. for entry in params:
  1679. if entry in user.channelallow:
  1680. user.channelallow.remove(entry)
  1681. for channel in bot.channels.itervalues():
  1682. if str(channel).endswith("_%s" % user.nick):
  1683. self.mode(channel, False, "I %s!*@*" % allowed) # Remove the invite-only exception
  1684. bot.send_and_log(recipient, user_executed,
  1685. "The requested users are no longer on your channelallow list.")
  1686. bot.changed()
  1687. else:
  1688. raise MissingParams
  1689. @command("List your channelallow list."
  1690. "channelallow list")
  1691. @toggleable_command
  1692. def channelallow_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1693. userdata = bot.check_for_master(user_executed)
  1694. if userdata.channelallow:
  1695. bot.send_and_log(recipient, user_executed,
  1696. "You allow the following nicks on your channel: %s."
  1697. % join_and(", ", " and ", userdata.channelallow))
  1698. else:
  1699. bot.send_and_log(recipient, user_executed,
  1700. "You don't allow any additional nicks on your channel.")
  1701. @command("Copy someone else's trigger topics and trigger words.\n"
  1702. "clone <nick> - Copies nick's trigger topics and trigger words to your nick.")
  1703. @protected_command
  1704. @toggleable_command
  1705. def clone(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1706. source_user = bot.check_for_master(bot.find_user(params[0]))
  1707. user = bot.check_for_master(user_executed)
  1708. user.trigger_words.update(source_user.trigger_words)
  1709. for topic, level in source_user.topics.iteritems():
  1710. user.topics[topic] = max(user.topics.get(topic, 0), level)
  1711. bot.changed()
  1712. bot.send_and_log(recipient, user_executed, "It is done.")
  1713. bot.update_rules()
  1714. @command("Manage your friends.\n"
  1715. "When someone on your friend list joins, you will get a notification. They will also be the first to be alerted by the 'panic' feature.")
  1716. @toggleable_command
  1717. def friend(bot, params, user, recipient, mainchannel, bypass=False):
  1718. raise BadCommand
  1719. @command("Add one or more user(s) to your friend list."
  1720. "friend add <user>")
  1721. @protected_command
  1722. @toggleable_command
  1723. def friend_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1724. if params:
  1725. user = bot.check_for_master(user_executed)
  1726. for entry in params:
  1727. if entry not in user.friends and entry != user_executed.nick and entry != user.nick and entry not in user.alts:
  1728. user.friends.append(entry)
  1729. bot.send_and_log(recipient, user_executed,
  1730. "The requested users were added to your friend list.")
  1731. bot.changed()
  1732. else:
  1733. raise MissingParams
  1734. @command("Remove one or more user(s) from your friend list."
  1735. "friend remove <user>")
  1736. @protected_command
  1737. @toggleable_command
  1738. def friend_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1739. if params:
  1740. user = bot.check_for_master(user_executed)
  1741. for entry in params:
  1742. if entry in user.friends:
  1743. user.friends.remove(entry)
  1744. bot.send_and_log(recipient, user_executed,
  1745. "The requested users are no longer on your friend list.")
  1746. bot.changed()
  1747. else:
  1748. raise MissingParams
  1749. @command("List your own or someone else's friend list."
  1750. "friend list <user>")
  1751. @toggleable_command
  1752. def friend_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1753. if params:
  1754. user_to_check = bot.find_user(params[0])
  1755. else:
  1756. user_to_check = user_executed
  1757. userdata = bot.check_for_master(user_to_check)
  1758. if userdata.friends:
  1759. bot.send_and_log(recipient, user_executed,
  1760. "%s has the following friends set: %s."
  1761. % (user_to_check, join_and(", ", " and ", userdata.friends)))
  1762. else:
  1763. bot.send_and_log(recipient, user_executed,
  1764. "%s has no friends set."
  1765. % user_to_check)
  1766. @command("Group usernames together.")
  1767. @toggleable_command
  1768. def group(bot, params, user, recipient, mainchannel, bypass=False):
  1769. raise BadCommand
  1770. @command("Set the main username this account should belong to. A password needs to be specified if the main username is protected.\n"
  1771. "group add <accountname> <password>")
  1772. @protected_command
  1773. @toggleable_command
  1774. def group_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1775. if params:
  1776. user = bot.find_user(params[0])
  1777. if user_executed != user:
  1778. if not user_executed.alts:
  1779. if str(user.master) != None:
  1780. if not user_executed.nick in user.alts:
  1781. if user.password and not bcrypt.hashpw(' '.join(params[1:]), user.password) == user.password:
  1782. bot.send_and_log(recipient, user_executed,
  1783. "The account you wanted to group with is password protected, and the password entered did not match.")
  1784. return
  1785. else:
  1786. user.alts.append(user_executed.nick)
  1787. else:
  1788. bot.send_and_log(recipient, user_executed,
  1789. "This account is already an alt of %s."
  1790. % params[0])
  1791. return
  1792. if user_executed.master and user_executed.master != user.nick:
  1793. master = bot.find_user(user_executed.master)
  1794. if user_executed.nick in master.alts:
  1795. master.alts.remove(user_executed.nick)
  1796. user_executed.master = params[0]
  1797. else:
  1798. bot.send_and_log(recipient, user_executed,
  1799. "%s is already an alt. You can only link an alt to an account which is not an alt itself."
  1800. % user.nick)
  1801. return
  1802. bot.changed()
  1803. bot.send_and_log(recipient, user_executed,
  1804. "You are now registered as an alt of %s."
  1805. % params[0])
  1806. else:
  1807. bot.send_and_log(recipient, user_executed,
  1808. "This account already has alts. It cannot become an alt itself.")
  1809. else:
  1810. # There always is that person who likes to see if they can break stuff...
  1811. bot.send_and_log(recipient, user_executed,
  1812. "You cannot link yourself with yourself.")
  1813. else:
  1814. raise MissingParams
  1815. @command("Remove the alt status from this account.\n"
  1816. "group remove")
  1817. @protected_command
  1818. @toggleable_command
  1819. def group_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1820. if user_executed.master:
  1821. user = bot.find_user(user_executed.master)
  1822. altlist = user.alts
  1823. if user_executed.nick in altlist:
  1824. altlist.remove(user_executed.nick)
  1825. user.alts = altlist
  1826. user_executed.master = None
  1827. else:
  1828. altlist = user_executed.alts
  1829. for user in altlist:
  1830. user = bot.find_user(user)
  1831. user.master = None
  1832. user_executed.alts = []
  1833. bot.changed()
  1834. bot.send_and_log(recipient, user_executed,
  1835. "This account is no longer grouped.")
  1836. @command("List alts and master data."
  1837. "group list <accountname>")
  1838. @toggleable_command
  1839. def group_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1840. if params:
  1841. user = bot.find_user(params[0])
  1842. else:
  1843. user = user_executed
  1844. master = ""
  1845. group = []
  1846. master = bot.check_for_master(user)
  1847. if master != user:
  1848. group += master.alts
  1849. if user in group:
  1850. group.remove(user)
  1851. master = master.nick
  1852. else:
  1853. if master.alts:
  1854. group += master.alts
  1855. if master != user and len(group) > 1:
  1856. bot.send_and_log(recipient, user_executed,
  1857. "%s belongs to a group owned by %s, together with %s."
  1858. % (user, master,
  1859. join_and(", ", " and ", group)))
  1860. elif master != user:
  1861. bot.send_and_log(recipient, user_executed,
  1862. "%s is the only alt of %s."
  1863. % (user, master))
  1864. elif group:
  1865. bot.send_and_log(recipient, user_executed,
  1866. "%s is the leader of a group containing %s."
  1867. % (user,
  1868. join_and(", ", " and ", group)))
  1869. else:
  1870. bot.send_and_log(recipient, user_executed,
  1871. "%s is not in any group."
  1872. % user)
  1873. @command("Lists the available commands.\n"
  1874. "help")
  1875. @toggleable_command
  1876. def help(bot, params, user, recipient, mainchannel, bypass=False):
  1877. disabledcommands = bot.get_settings().disabledcommands
  1878. subcommand_doc = []
  1879. for subcommand, description in bot.subcommands(params):
  1880. if params:
  1881. checkfor = "%s_%s" % ("_".join(params), subcommand)
  1882. else:
  1883. checkfor = subcommand
  1884. if checkfor in disabledcommands:
  1885. continue
  1886. subcommand_doc.append(" %s - %s" %
  1887. (subcommand,
  1888. description.split("\n", 1)[0]
  1889. if description else "(no description)"))
  1890. subcommand_doc.sort()
  1891. if params:
  1892. for commandcore in range(1, len(params)+1):
  1893. if "_".join(params[:commandcore]) in disabledcommands:
  1894. raise CommandNotFound(" ".join(params))
  1895. lines = []
  1896. try:
  1897. description = bot.command_description(params)
  1898. except KeyError:
  1899. raise CommandNotFound(" ".join(params))
  1900. if description is not None:
  1901. lines += (" ".join(params) + " - " + description).split("\n")
  1902. if subcommand_doc:
  1903. lines += ["'%s' has the following sub-commands:" % " ".join(params)] \
  1904. + subcommand_doc \
  1905. + ["Use 'help %s <sub-command>' for more information"
  1906. " about a particular sub-command." % " ".join(params)]
  1907. else:
  1908. lines = ["The following commands are available:"] \
  1909. + subcommand_doc \
  1910. + ["Use 'help <command>' to show information about a particular command."]
  1911. for line in lines:
  1912. bot.send_and_log(user, user, line)
  1913. @command("Tell the bot you're being helped.\n"
  1914. "helped")
  1915. def helped(bot, params, user_executed, recipient, resethelp=False, bypass=False):
  1916. user = bot.check_for_master(user_executed)
  1917. if not resethelp:
  1918. user.helped = True
  1919. reactor.callLater(120, helped, bot=bot, params=params, user_executed=user_executed, recipient=recipient, resethelp=True)
  1920. bot.send_and_log(recipient, user_executed, "I'm glad you're being helped. Please, feel better soon!")
  1921. else:
  1922. user.helped = False
  1923. @command("Gives someone a hug!\n"
  1924. "hug")
  1925. def hug(bot, params, user, recipient, mainchannel, bypass=False):
  1926. if params:
  1927. huggee = " ".join(params)
  1928. if huggee.lower() == "me":
  1929. huggee = user
  1930. else:
  1931. huggee = user
  1932. templates = [
  1933. "slowly and softly puts its arms around %s and hugs them.",
  1934. "glomps %s.",
  1935. "wraps %s in bot arms and squeezes gently.",
  1936. "cuddles %s like a plushie."
  1937. ]
  1938. message = random.choice(templates) % huggee
  1939. bot.describe(recipient, message)
  1940. bot.logger.log("[%s] * %s %s" % (recipient, bot.nickname, message))
  1941. @command("Log in to a password-protected account.\n"
  1942. "Required to make changes to the account trigger topics, trigger words or settings if protected.\n"
  1943. "identify <password>")
  1944. @toggleable_command
  1945. def identify(bot, params, user, recipient, mainchannel, bypass=False):
  1946. master = bot.check_for_master(user)
  1947. if master.password != None:
  1948. if bcrypt.hashpw(' '.join(params), master.password) == master.password:
  1949. bot.send_and_log(recipient, user,
  1950. "You are now logged in.")
  1951. user.logged_in = True
  1952. else:
  1953. bot.send_and_log(recipient, user,
  1954. "Sorry, but the password you entered is incorrect. Please try again.")
  1955. else:
  1956. bot.send_and_log(recipient, user,
  1957. "This username is not protected.")
  1958. @command("Manage your ignore list.")
  1959. def ignore(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1960. raise BadCommand
  1961. @command("Add one or more users to your ignore list.\n"
  1962. "ignore add <user>")
  1963. def ignore_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1964. if params:
  1965. user = bot.check_for_master(user_executed)
  1966. for param in params:
  1967. ignored = bot.check_for_master(bot.find_user(param))
  1968. if not ignored.nick in user.ignore:
  1969. user.ignore.append(ignored.nick)
  1970. if not user.nick in ignored.ignoredby:
  1971. ignored.ignoredby.append(user.nick)
  1972. bot.send_and_log(recipient, user_executed,
  1973. "The requested %s added to your ignore list" %
  1974. ("users were" if len(params) > 1 else "user was"))
  1975. bot.changed()
  1976. else:
  1977. raise MissingParams
  1978. @command("Remove one or more users from your ignore list.\n"
  1979. "ignore remove <user>")
  1980. def ignore_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1981. if params:
  1982. user = bot.check_for_master(user_executed)
  1983. for param in params:
  1984. ignored = bot.check_for_master(bot.find_user(param))
  1985. if ignored.nick in user.ignore:
  1986. user.ignore.remove(ignored.nick)
  1987. if user.nick in ignored.ignoredby:
  1988. ignored.ignoredby.remove(user.nick)
  1989. bot.send_and_log(recipient, user_executed,
  1990. "The requested %s removed from your ignore list" %
  1991. ("users were" if len(params) > 1 else "user was"))
  1992. bot.changed()
  1993. else:
  1994. raise MissingParams
  1995. @command("List your ignore list.\n"
  1996. "ignore list")
  1997. def ignore_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  1998. user = bot.check_for_master(user_executed)
  1999. if user.ignore:
  2000. bot.send_and_log(recipient, user_executed,
  2001. "The following users are on your ignore list: %s" %
  2002. (", ".join(user.ignore)))
  2003. else:
  2004. bot.send_and_log(recipient, user_executed,
  2005. "Your ignore list is currently empty.")
  2006. @command("Log out of your account.\n"
  2007. "logout")
  2008. @protected_command
  2009. @toggleable_command
  2010. def logout(bot, params, user, recipient, mainchannel, bypass=False):
  2011. bot.send_and_log(recipient, user,
  2012. "You are now logged out.")
  2013. user.logged_in = False
  2014. @command("Manage or send mail.")
  2015. @protected_command
  2016. @toggleable_command
  2017. def mail(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2018. raise BadCommand
  2019. @command("List messages in your inbox.\n"
  2020. "Filter will take either 'read', 'unread' or 'all'. Anything other is considered a search.\n"
  2021. "mail inbox [<filter>]")
  2022. @protected_command
  2023. @toggleable_command
  2024. def mail_inbox(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2025. user = bot.check_for_master(user_executed)
  2026. if user.messages:
  2027. messagelist = sorted(user.messages.keys())
  2028. timelength = 8
  2029. senderlength = 6
  2030. tosend = []
  2031. for number, message in enumerate(messagelist):
  2032. messageid = number+1
  2033. time = TimeFormat().date(time=messagelist[number])
  2034. sender = str(user.messages[message][0])
  2035. messagehidden = False
  2036. if not params:
  2037. tosend.append("%s%s | %s | %s | %s"
  2038. % (" " if number < 9 else "", number+1, time, sender, "yes" if message in user.readmessages else "no"))
  2039. elif params and params[0] in ["unread", "all"] and not message in user.readmessages:
  2040. tosend.append("%s%s | %s | %s | %s"
  2041. % (" " if number < 9 else "", number+1, time, sender, "no"))
  2042. elif params and params[0] in ["read", "all"] and message in user.readmessages:
  2043. tosend.append("%s%s | %s | %s | %s"
  2044. % (" " if number < 9 else "", number+1, time, sender, "yes"))
  2045. else:
  2046. messagehidden = True
  2047. if messagehidden:
  2048. continue
  2049. if len(time) > timelength:
  2050. timelength = len(time)
  2051. if len(sender) > senderlength:
  2052. senderlength = len(sender)
  2053. if not tosend:
  2054. bot.send_and_log(recipient, user_executed,
  2055. "There are no messages matching your filter: 'only messages marked as %s'." % (params[0]))
  2056. return
  2057. bot.send_and_log(recipient, user_executed,
  2058. "Tip: To read a message, type 'mail read', followed by the message id.")
  2059. bot.send_and_log(recipient, user_executed,
  2060. "id | %s | %s | read" % ("received".center(timelength), "sender".center(senderlength)))
  2061. for abouttosend in tosend:
  2062. abouttosend = "%s | %s | %s | %s" % (abouttosend.split(" | ")[0], abouttosend.split(" | ")[1].center(timelength), abouttosend.split(" | ")[2].center(senderlength), abouttosend.split(" | ")[3])
  2063. bot.send_and_log(recipient, user_executed, abouttosend)
  2064. else:
  2065. bot.send_and_log(recipient, user_executed,
  2066. "There are no messages for you.")
  2067. @command("Mark one or more mails as read or unread.\n"
  2068. "By default, all mails are marked this way.\n"
  2069. "mail mark <read/unread> [<id>]")
  2070. def mail_mark(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2071. raise BadCommand
  2072. @command("Mark one or more mails as read.\n"
  2073. "By default, all mails are marked as read.\n"
  2074. "mail mark read [<id>]")
  2075. @protected_command
  2076. @toggleable_command
  2077. def mail_mark_read(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2078. user = bot.check_for_master(user_executed)
  2079. messagelist = sorted(user.messages.keys())
  2080. if not params:
  2081. for message in messagelist:
  2082. if message not in user.readmessages:
  2083. user.readmessages.append(message)
  2084. else:
  2085. for param in params:
  2086. try:
  2087. number = int(param) - 1
  2088. except ValueError:
  2089. bot.send_and_log(recipient, user_executed,
  2090. "When set, this parameter needs to be a number.")
  2091. return
  2092. if not messagelist[number] in user.readmessages:
  2093. user.readmessages.append(messagelist[number])
  2094. bot.send_and_log(recipient, user_executed,
  2095. "Marked %s %s as read" % ("all" if not params else len(params), "message" if len(params) == 1 else "messages"))
  2096. @command("Mark one or more mails as unread.\n"
  2097. "By default, all mails are marked as unread.\n"
  2098. "mail mark unread [<id>]")
  2099. @protected_command
  2100. @toggleable_command
  2101. def mail_mark_unread(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2102. user = bot.check_for_master(user_executed)
  2103. messagelist = sorted(user.messages.keys())
  2104. if not params:
  2105. for message in messagelist:
  2106. if message in user.readmessages:
  2107. user.readmessages.remove(message)
  2108. else:
  2109. for param in params:
  2110. try:
  2111. number = int(param) - 1
  2112. except ValueError:
  2113. bot.send_and_log(recipient, user_executed,
  2114. "When set, this parameter needs to be a number.")
  2115. return
  2116. if messagelist[number] in user.readmessages:
  2117. user.readmessages.remove(messagelist[number])
  2118. bot.send_and_log(recipient, user_executed,
  2119. "Marked %s %s as unread" % ("all" if not params else len(params), "message" if len(params) == 1 else "messages"))
  2120. @command("Read a message.\n"
  2121. "By default, all unread messages are shown.\n"
  2122. "mail read [<id>]")
  2123. @protected_command
  2124. @toggleable_command
  2125. def mail_read(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2126. try:
  2127. number = int(params[0]) - 1
  2128. except ValueError:
  2129. bot.send_and_log(recipient, user_executed,
  2130. "When set, this parameter needs to be a number.")
  2131. except IndexError:
  2132. pass
  2133. if params and number < 0:
  2134. raise MessageNotFound(number+1)
  2135. user = bot.check_for_master(user_executed)
  2136. messagelist = sorted(user.messages.keys())
  2137. if params:
  2138. try:
  2139. messageid = messagelist[number]
  2140. bot.send_and_log(recipient, user_executed,
  2141. "[%s] %s: %s" % (number+1, user.messages[messageid][0], user.messages[messageid][1]))
  2142. if not messageid in user.readmessages:
  2143. user.readmessages.append(messageid)
  2144. except IndexError:
  2145. raise MessageNotFound(number+1)
  2146. elif user.messages:
  2147. messagesfound = False
  2148. for message in messagelist:
  2149. if message in user.readmessages:
  2150. continue
  2151. user.readmessages.append(message)
  2152. messagesfound = True
  2153. bot.send_and_log(recipient, user_executed,
  2154. "[%s] %s: %s" % (number+1, user.messages[message][0], user.messages[message][1]))
  2155. if not messagesfound:
  2156. bot.send_and_log(recipient, user_executed,
  2157. "Sorry, I could not find any unread messages.")
  2158. else:
  2159. bot.send_and_log(recipient, user_executed,
  2160. "Sorry, I could not find any messages.")
  2161. @command("Remove a message from your inbox.\n"
  2162. "Then id parameter also takes the value 'all', which purges all messages.\n"
  2163. "mail remove <id>")
  2164. @protected_command
  2165. @toggleable_command
  2166. def mail_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2167. if not params:
  2168. raise MissingParams
  2169. user = bot.check_for_master(user_executed)
  2170. if params[0] == 'all':
  2171. user.messages = {}
  2172. user.readmessages = []
  2173. bot.send_and_log(recipient, user_executed,
  2174. "All your mail has been removed.")
  2175. else:
  2176. messagelist = sorted(user.messages.keys())
  2177. try:
  2178. number = int(params[0]) - 1
  2179. except ValueError:
  2180. bot.send_and_log(recipient, user_executed,
  2181. "This parameter needs to be a number or the value 'all'.")
  2182. try:
  2183. del user.messages[messagelist[number]]
  2184. except IndexError:
  2185. raise MessageNotFound(number+1)
  2186. if messagelist[number] in user.readmessages:
  2187. user.readmessages.remove(messagelist[number])
  2188. bot.send_and_log(recipient, user_executed,
  2189. "Message removed.")
  2190. @command("Send a mail to an user for when they log in.\n"
  2191. "mail send <user> <message>")
  2192. @protected_command
  2193. @toggleable_command
  2194. def mail_send(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2195. if len(params) > 1:
  2196. user = bot.check_for_master(bot.find_user(params[0]))
  2197. message = str(" ".join(params[1:]))
  2198. safe = bot.is_safe(message=message, user=user)
  2199. if not safe[0]:
  2200. bot.send_and_log(recipient, user_executed,
  2201. "Sorry, but your message to %s was not sent due to it possibly being unsafe" % params[0])
  2202. return
  2203. user.messages[datetime.datetime.now()] = user_executed, message
  2204. bot.send_and_log(recipient, user_executed,
  2205. "Your message to %s was sent succesfully." % params[0])
  2206. # Message the user and their alts. Why let them wait until they log in if they're there?
  2207. receiverchannels = []
  2208. receivers = [user]
  2209. for alt in user.alts:
  2210. receivers.append(bot.find_user(alt))
  2211. for channel in bot.channels:
  2212. channeldata = bot.get_channel(channel)
  2213. for receiver in receivers[:]:
  2214. if receiver in channeldata.users:
  2215. if channeldata in receiverchannels:
  2216. continue
  2217. bot.send_and_log(channeldata, receiver,
  2218. "You have received a new message. Please check your unread messages using '!mail inbox unread'.")
  2219. receivers.remove(receiver)
  2220. receiverchannels.append(channeldata)
  2221. for receiver in receivers:
  2222. bot.send_and_log(receiver, receiver,
  2223. "You have received a new message. Please check your unread messages using '!mail inbox unread'.")
  2224. else:
  2225. raise MissingParams
  2226. @command("Manage your channel mode(s).")
  2227. @toggleable_command
  2228. def mode(bot, params, user, recipient, mainchannel, bypass=False):
  2229. raise BadCommand
  2230. @command("List the current channel modes.\n"
  2231. "mode list")
  2232. @toggleable_command
  2233. def mode_list(bot, params, user, recipient, mainchannel, bypass=False):
  2234. modes = recipient.mode
  2235. if "default" in modes:
  2236. modes.remove("default")
  2237. if modes:
  2238. bot.send_and_log(recipient, user,
  2239. "This channel has the following %s set: %s" %
  2240. ("modes" if len(modes) > 1 else "mode", ", ".join(modes)))
  2241. else:
  2242. bot.send_and_log(recipient, user,
  2243. "This channel is in the default mode.")
  2244. @command("Add a mode to your channel.\n"
  2245. "mode add <mode>")
  2246. @toggleable_command
  2247. def mode_add(bot, params, user, recipient, mainchannel, bypass=False):
  2248. raise BadCommand
  2249. @command("Remove a mode from your channel.\n"
  2250. "mode remove <mode>")
  2251. @toggleable_command
  2252. def mode_remove(bot, params, user, recipient, mainchannel, bypass=False):
  2253. raise BadCommand
  2254. @command("Do not filter any incoming messages, even if they contain triggerwords or subjects.\n"
  2255. "mode add filterless")
  2256. @protected_command
  2257. @toggleable_command
  2258. def mode_add_filterless(bot, params, user, recipient, mainchannel, bypass=False):
  2259. for incompatible in ["silent"]:
  2260. if incompatible in recipient.mode:
  2261. recipient.mode.remove(incompatible)
  2262. if not "filterless" in recipient.mode:
  2263. recipient.mode.append("filterless")
  2264. bot.send_and_log(recipient, user,
  2265. "Mode filterless added.")
  2266. bot.update_rules(recipient)
  2267. else:
  2268. bot.send_and_log(recipient, user,
  2269. "This channel is already in filterless mode.")
  2270. @command("Filter incoming messages if they contain triggerwords or subjects.\n"
  2271. "mode remove filterless")
  2272. @protected_command
  2273. @toggleable_command
  2274. def mode_remove_filterless(bot, params, user, recipient, mainchannel, bypass=False):
  2275. if "filterless" in recipient.mode:
  2276. recipient.mode.remove("filterless")
  2277. bot.send_and_log(recipient, user,
  2278. "Mode filterless removed.")
  2279. bot.update_rules(recipient)
  2280. else:
  2281. bot.send_and_log(recipient, user,
  2282. "This channel was not in filterless mode.")
  2283. @command("Give additional freedom of speech by bypassing triggerbot's rules. Your messages simply will not be sent to anyone sensitive to anything.\n"
  2284. "Incoming messages from users sensitives to anything will be filtered, as they're most likely not related to your rant."
  2285. "mode add rant")
  2286. @protected_command
  2287. @toggleable_command
  2288. def mode_add_rant(bot, params, user, recipient, mainchannel, bypass=False):
  2289. for incompatible in ["silent"]:
  2290. if incompatible in recipient.mode:
  2291. recipient.mode.remove(incompatible)
  2292. if not "rant" in recipient.mode:
  2293. recipient.mode.append("rant")
  2294. bot.send_and_log(recipient, user,
  2295. "Mode rant added.")
  2296. bot.update_rules(recipient)
  2297. else:
  2298. bot.send_and_log(recipient, user,
  2299. "This channel is already in rant mode.")
  2300. @command("Relay your messages to everyone, even those sensitive. This means that you are forced to stick to the triggerbot rules.\n"
  2301. "mode remove rant")
  2302. @protected_command
  2303. @toggleable_command
  2304. def mode_remove_rant(bot, params, user, recipient, mainchannel, bypass=False):
  2305. if "rant" in recipient.mode:
  2306. recipient.mode.remove("rant")
  2307. bot.send_and_log(recipient, user,
  2308. "Mode rant removed.")
  2309. bot.update_rules(recipient)
  2310. else:
  2311. bot.send_and_log(recipient, user,
  2312. "This channel is not in rant mode.")
  2313. @command("Disable any messages from leaving or entering your channel.\n"
  2314. "mode add silent")
  2315. @protected_command
  2316. @toggleable_command
  2317. def mode_add_silent(bot, params, user, recipient, mainchannel, bypass=False):
  2318. for incompatible in ["filterless", "rant"]:
  2319. if incompatible in recipient.mode:
  2320. recipient.mode.remove(incompatible)
  2321. if not "silent" in recipient.mode:
  2322. recipient.mode.append("silent")
  2323. bot.send_and_log(recipient, user,
  2324. "Relaying disabled.")
  2325. bot.relay("%s has left (Relaying disabled)." % user, recipient, relateduser=user, chat=False)
  2326. if not params:
  2327. bot.update_rules(recipient)
  2328. elif not params:
  2329. bot.send_and_log(recipient, user,
  2330. "This channel is already in silent mode.")
  2331. @command("Accept incoming messages and relay outgoing messages.\n"
  2332. "mode remove silent")
  2333. @protected_command
  2334. @toggleable_command
  2335. def mode_remove_silent(bot, params, user, recipient, mainchannel, bypass=False):
  2336. if "silent" in recipient.mode:
  2337. recipient.mode.remove("silent")
  2338. bot.send_and_log(recipient, user,
  2339. "Relaying enabled.")
  2340. bot.relay("%s has joined (Relaying enabled)." % user, recipient, relateduser=user, chat=False, notifyfriends=True)
  2341. if not params:
  2342. bot.update_rules(recipient)
  2343. elif not params:
  2344. bot.send_and_log(recipient, user,
  2345. "This channel is not in silent mode.")
  2346. @command("Reset your channel mode.\n"
  2347. "mode reset")
  2348. @protected_command
  2349. @toggleable_command
  2350. def mode_reset(bot, params, user, recipient, mainchannel, bypass=False):
  2351. recipient.mode = []
  2352. bot.send_and_log(recipient, user,
  2353. "Channel mode reset.")
  2354. bot.update_rules(recipient)
  2355. @command("Get a list of users in channels.\n"
  2356. "names")
  2357. @toggleable_command
  2358. def names(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2359. if len(params) > 0:
  2360. channel = bot.get_channel(params[0])
  2361. if not is_channel_name(channel.name):
  2362. channel.name = "#%s" % channel.name
  2363. else:
  2364. channel = recipient
  2365. if not getattr(channel, "is_channel", False):
  2366. bot.send_and_log(recipient, user,
  2367. "Please specify a channel to see the rules for.")
  2368. return
  2369. users = []
  2370. # In case it's a triggersafe channel, get the base channel
  2371. basechannel = str(channel).split("_")[0]
  2372. for checkchannel in bot.channels.itervalues():
  2373. if "silent" in checkchannel.mode:
  2374. continue
  2375. if str(checkchannel) == basechannel or str(checkchannel).startswith("%s_" % basechannel):
  2376. for user in checkchannel.users:
  2377. if user.nick in ["ChanServ", "NickServ"]:
  2378. continue
  2379. if not user.nick in users:
  2380. users.append(user.nick)
  2381. users.sort()
  2382. bot.send_and_log(recipient, None,
  2383. "Nicks %s: [%s]" % (recipient, " ".join(users)))
  2384. @command("Ask if someone is available to comfort you.\n"
  2385. "panic")
  2386. def panic(bot, params, user_executed, recipient, alert_channel_if_not_helped=False, bypass=False):
  2387. user = bot.check_for_master(user_executed)
  2388. if user.friends and not alert_channel_if_not_helped:
  2389. userfound = False
  2390. userlist = []
  2391. for channel in bot.channels.itervalues():
  2392. for person in channel.users:
  2393. userlist.append(person)
  2394. for person.nick in user.friends:
  2395. if person in userlist:
  2396. bot.send_and_log(person, None, "%s isn't feeling so well and would like you to comfort them." % user_executed)
  2397. bot.send_and_log(person, None, "If you can hear them out, please type '/query %s' to start a private conversation with them." % user_executed)
  2398. userfound = True
  2399. if userfound:
  2400. reactor.callLater(120, panic, bot=bot, params=params, user_executed=user_executed, recipient=recipient, alert_channel_if_not_helped=True)
  2401. bot.send_and_log(recipient, user_executed, None, "Please type '!helped' if you're being helped. If nobody is there to help you in 2 minutes, I will search for more help. Everything will be fine, %s."
  2402. % user_executed)
  2403. else:
  2404. panic(bot=bot, params=params, user_executed=user_executed, recipient=recipient, alert_channel_if_not_helped=True)
  2405. elif user.helped == False:
  2406. bot.send_and_log(recipient, user_executed, "I'm searching for more help for you. Please try to hold on, %s." % user_executed)
  2407. for channel in bot.channels.itervalues():
  2408. if is_channel_name(str(channel.name)):
  2409. bot.send_and_log(channel, None, "%s isn't feeling so well and would like someone to comfort them." % user_executed)
  2410. bot.send_and_log(channel, None, "If you can hear them out, please type '/query %s' to start a private conversation with them." % user_executed)
  2411. @command("Lists user permissions.\n"
  2412. "permission - List which admin commands you can execute.\n"
  2413. "permission <nick> - Lists the admin commands nick can execute.")
  2414. @toggleable_command
  2415. def permission(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2416. user = bot.check_for_master(user_executed)
  2417. queried_user = bot.find_user(params[0]) if len(params) > 0 else user
  2418. if queried_user.admin:
  2419. bot.send_and_log(recipient, user_executed,
  2420. "%s can execute all admin commands." % queried_user.nick)
  2421. else:
  2422. if queried_user.admincommandsallowed:
  2423. bot.send_and_log(recipient, user_executed,
  2424. "%s can execute the following admin commands: %s."
  2425. % (queried_user.nick,
  2426. join_and(", ", " and ",
  2427. ("%r" % " ".join(command.split("_")) for command in queried_user.admincommandsallowed))))
  2428. else:
  2429. bot.send_and_log(recipient, user_executed,
  2430. "%s can not execute any admin commands." % queried_user.nick)
  2431. @command("Lists the current rules.\n"
  2432. "Triggerbot automatically changes the channel rules according to"
  2433. " the trigger topics of the people present. Use this command if you"
  2434. " are ever unsure about the current rules.\n"
  2435. "rules [<channel>]")
  2436. @toggleable_command
  2437. def rules(bot, params, user, recipient, mainchannel, bypass=False):
  2438. if len(params) > 0:
  2439. channel = bot.get_channel(params[0])
  2440. if not is_channel_name(channel.name):
  2441. channel.name = "#%s" % channel.name
  2442. else:
  2443. channel = recipient
  2444. if not getattr(channel, "is_channel", False):
  2445. bot.send_and_log(recipient, user,
  2446. "Please specify a channel to see the rules for.")
  2447. return
  2448. bot.report_rules(channel=channel, user=user, recipient=recipient, changed=True)
  2449. @command("Get info on when triggerbot last saw a specific person.\n"
  2450. "seen <nickname>")
  2451. @toggleable_command
  2452. def seen(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2453. if params:
  2454. if params[0] != bot.nickname:
  2455. user = bot.check_for_master(bot.find_user(params[0]))
  2456. lastseen = TimeFormat().date(time=user.seen)
  2457. else:
  2458. lastseen = "now, forever ago and forever in the future"
  2459. bot.send_and_log(recipient, user_executed,
  2460. "I have last seen %s %s." %
  2461. (params[0], lastseen))
  2462. else:
  2463. raise MissingParams
  2464. @command("Sets various settings for you.")
  2465. @toggleable_command
  2466. def set(bot, params, user, recipient, mainchannel, bypass=False):
  2467. raise BadCommand
  2468. @command("Disables a setting for you.")
  2469. @toggleable_command
  2470. def unset(bot, params, user, recipient, mainchannel, bypass=False):
  2471. raise BadCommand
  2472. @command("When set, triggerbot doesn't apply your trigger topics and"
  2473. " words if you are marked away.\n"
  2474. "set awaycheck")
  2475. @protected_command
  2476. @toggleable_command
  2477. def set_awaycheck(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2478. user = bot.check_for_master(user_executed)
  2479. user.awaycheck = True
  2480. bot.send_and_log(recipient, user_executed,
  2481. "Awaycheck set.")
  2482. bot.changed()
  2483. bot.update_rules()
  2484. @command("When unset, triggerbot applies your trigger topics and"
  2485. " words, even if you are marked away.\n"
  2486. "unset awaycheck")
  2487. @protected_command
  2488. @toggleable_command
  2489. def unset_awaycheck(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2490. user = bot.check_for_master(user_executed)
  2491. user.awaycheck = False
  2492. bot.send_and_log(recipient, user_executed,
  2493. "Awaycheck unset.")
  2494. bot.changed()
  2495. bot.update_rules()
  2496. @command("when set, triggerbot will silence your channel when you"
  2497. " go away (requires awaycheck to be set).\n"
  2498. "set autosilence")
  2499. @protected_command
  2500. @toggleable_command
  2501. def set_autosilence(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2502. user = bot.check_for_master(user_executed)
  2503. user.autosilence = True
  2504. bot.send_and_log(recipient, user_executed,
  2505. "autosilence set.")
  2506. bot.changed()
  2507. @command("when unset, triggerbot will not silence your channel when you"
  2508. " go away (requires awaycheck to be set).\n"
  2509. "unset autosilence")
  2510. @protected_command
  2511. @toggleable_command
  2512. def unset_autosilence(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2513. user = bot.check_for_master(user_executed)
  2514. user.autosilence = False
  2515. bot.send_and_log(recipient, user_executed,
  2516. "autosilence set.")
  2517. bot.changed()
  2518. @command("When set, triggerbot will automatically log you out when you quit"
  2519. " or leave all channels the bot is in.\n"
  2520. "set autologout")
  2521. @protected_command
  2522. @toggleable_command
  2523. def set_autologout(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2524. user = bot.check_for_master(user_executed)
  2525. user.autologout = True
  2526. bot.send_and_log(recipient, user_executed,
  2527. "Autologout set.")
  2528. bot.changed()
  2529. @command("When unset, triggerbot will keep you logged in, even if you"
  2530. " quit or leave all channels the bot is in.\n"
  2531. "unset autologout")
  2532. @protected_command
  2533. @toggleable_command
  2534. def unset_autologout(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2535. user = bot.check_for_master(user_executed)
  2536. user.autologout = False
  2537. bot.send_and_log(recipient, user_executed,
  2538. "Autologout unset.")
  2539. bot.changed()
  2540. @command("When set, triggerbot will not report your own triggers in"
  2541. " your own triggersafe channels.\n"
  2542. "set hideown")
  2543. @protected_command
  2544. @toggleable_command
  2545. def set_hideown(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2546. user = bot.check_for_master(user_executed)
  2547. user.hideown = True
  2548. bot.send_and_log(recipient, user_executed,
  2549. "Your triggers will no longer be displayed in your own channel(s).")
  2550. bot.changed()
  2551. @command("When unset, triggerbot will also report your own triggers in"
  2552. " your triggersafe channels.\n"
  2553. "unset hideown")
  2554. @protected_command
  2555. @toggleable_command
  2556. def unset_hideown(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2557. user = bot.check_for_master(user_executed)
  2558. user.hideown = False
  2559. bot.send_and_log(recipient, user_executed,
  2560. "Your triggers will be displayed in your own channel(s).")
  2561. bot.changed()
  2562. @command("When set, triggerbot will not see any of your sentences in a"
  2563. " private chat as commands and will listen to you like"
  2564. " a therapist would.\n"
  2565. "set listenmode")
  2566. @protected_command
  2567. @toggleable_command
  2568. def set_listenmode(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2569. user = bot.check_for_master(user_executed)
  2570. user.listenmode = True
  2571. bot.send_and_log(recipient, user_executed,
  2572. "Listenmode set.")
  2573. bot.changed()
  2574. @command("When unset, triggerbot will see your messages in a"
  2575. " private channel as commands.\n"
  2576. "unset listenmode")
  2577. @protected_command
  2578. @toggleable_command
  2579. def unset_listenmode(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2580. user = bot.check_for_master(user_executed)
  2581. user.listenmode = False
  2582. bot.send_and_log(recipient, user_executed,
  2583. "Listenmode unset.")
  2584. bot.changed()
  2585. @command("When set, the MOTD will not be displayed in your channel.\n"
  2586. "set motdread")
  2587. @protected_command
  2588. @toggleable_command
  2589. def set_motdread(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2590. user = bot.check_for_master(user_executed)
  2591. user.motdread = True
  2592. bot.send_and_log(recipient, user_executed,
  2593. "MOTD marked as read. The channel topic for your triggersafe channel(s) will be updated soon.")
  2594. for channel in bot.channels:
  2595. if channel.endswith("_%s" % user.nick.lower()):
  2596. setattr(bot.get_channel(channel), "topicset", "%s's triggersafe channel. | [rules][mode]" % user.nick)
  2597. bot.changed()
  2598. @command("When unset, the MOTD will be displayed in your channel.\n"
  2599. "unset motdread")
  2600. @protected_command
  2601. @toggleable_command
  2602. def unset_motdread(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2603. user = bot.check_for_master(user_executed)
  2604. user.motdread = False
  2605. if recipient:
  2606. bot.send_and_log(recipient, user_executed,
  2607. "MOTD marked as unread. The channel topic for your triggersafe channel(s) will be updated soon.")
  2608. for channel in bot.channels:
  2609. if channel.endswith("_%s" % user.nick.lower()):
  2610. setattr(bot.get_channel(channel), "topicset", "%s's triggersafe channel. | [globalmotd][rules][mode]" % user.nick)
  2611. bot.changed()
  2612. @command("When set, being logged in with NickServ will log you in with triggerbot.\n"
  2613. "set nickservlogin")
  2614. @protected_command
  2615. @toggleable_command
  2616. def set_nickservlogin(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2617. user = bot.check_for_master(user_executed)
  2618. user.nickservlogin = False
  2619. bot.send_and_log(recipient, user_executed,
  2620. "NickServ logins will now cause triggerbot logins.")
  2621. @command("When unset, being logged in with NickServ will not log you in with triggerbot.\n"
  2622. "unset nickservlogin")
  2623. @protected_command
  2624. @toggleable_command
  2625. def unset_nickservlogin(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2626. user = bot.check_for_master(user_executed)
  2627. user.nickservlogin = True
  2628. bot.send_and_log(recipient, user_executed,
  2629. "NickServ logins will no long cause triggerbot logins.")
  2630. @command("When set, your account becomes password protected and"
  2631. " trigger topics, triggerwords and setting cannot be changed"
  2632. " without logging in first.\n"
  2633. "set password <password>")
  2634. @protected_command
  2635. @toggleable_command
  2636. def set_password(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2637. user = bot.check_for_master(user_executed)
  2638. user.password = bcrypt.hashpw(' '.join(params), bcrypt.gensalt())
  2639. bot.send_and_log(recipient, user_executed,
  2640. "Password set.")
  2641. bot.changed()
  2642. @command("When unset, your account will not be password protected.\n"
  2643. "unset password")
  2644. @protected_command
  2645. @toggleable_command
  2646. def unset_password(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2647. master = bot.check_for_master(user_executed)
  2648. master.password = None
  2649. master.logged_in = False
  2650. for user in master.alts:
  2651. user.logged_in = False
  2652. bot.send_and_log(recipient, user_executed,
  2653. "Password protection removed.")
  2654. bot.changed()
  2655. @command("When set, triggerbot will keep a trigger-safe copy available"
  2656. " for each of the channels for you, in which all messages"
  2657. " without triggerwords will be relayed.\n"
  2658. "This channel name will be like #channelname_yournickname. So,"
  2659. " if your nickname is example, a filtered version of"
  2660. " #examplechannel is available for you at"
  2661. " #examplechannel_example.\n"
  2662. "set channel")
  2663. @protected_command
  2664. @toggleable_command
  2665. def set_channel(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2666. user = bot.check_for_master(user_executed)
  2667. user.channel = True
  2668. for channel in bot.channels.keys():
  2669. if is_channel_name(channel):
  2670. userchannel = False
  2671. try:
  2672. if channel.split("_")[1]:
  2673. userchannel = True
  2674. except IndexError:
  2675. pass
  2676. if not userchannel:
  2677. tocheck = "%s_%s" % (channel, user.nick)
  2678. tocheck = tocheck.lower()
  2679. if not tocheck in bot.channels.keys():
  2680. bot.join_channel(tocheck)
  2681. bot.send_and_log(recipient, user_executed,
  2682. "Trigger-safe channels are now available for you.")
  2683. bot.changed()
  2684. @command("When unset, triggerbot will no longer keep a trigger-safe"
  2685. " copy of each of the channels available for you.\n"
  2686. "unset channel")
  2687. @protected_command
  2688. @toggleable_command
  2689. def unset_channel(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2690. user = bot.check_for_master(user_executed)
  2691. user.channel = False
  2692. for channel in bot.channels.keys():
  2693. if is_channel_name(channel):
  2694. userchannel = False
  2695. try:
  2696. if channel.split("_")[1]:
  2697. userchannel = True
  2698. except IndexError:
  2699. pass
  2700. if not userchannel:
  2701. tocheck = "%s_%s" % (channel, user.nick)
  2702. tocheck = tocheck.lower()
  2703. if tocheck in bot.channels.keys():
  2704. bot.leave_channel(tocheck, unregister=True)
  2705. bot.send_and_log(recipient, user_executed,
  2706. "Trigger-safe channels are no longer available for you.")
  2707. bot.changed()
  2708. @command("Check the status of an account option.\n"
  2709. "status <option>")
  2710. @toggleable_command
  2711. def status(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2712. user = bot.check_for_master(user_executed)
  2713. try:
  2714. if getattr(user, params[0]) in (None, False):
  2715. bot.send_and_log(recipient, user,
  2716. "The option %s is currently unset." % params[0])
  2717. else:
  2718. bot.send_and_log(recipient, user_executed,
  2719. "The option %s is currently set." % params[0])
  2720. except AttributeError:
  2721. bot.send_and_log(recipient, user_executed,
  2722. "Sorry, but I could not find the option %s." % params[0])
  2723. @command("Display a link to triggerbot's source code.\n"
  2724. "source")
  2725. def source(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2726. bot.send_and_log(recipient, user_executed,
  2727. "%s's source code is available on %s and %s" % (bot.nickname, bot.sourceURLs[0], bot.sourceURLs[1]))
  2728. @command("Manage topics which make you feel uncomfortables.")
  2729. @toggleable_command
  2730. def topic(bot, params, user, recipient, mainchannel, bypass=False):
  2731. raise BadCommand
  2732. @command("Adds one or more trigger topic(s) for you.\n"
  2733. "topic add <topic> <level>")
  2734. @protected_command
  2735. @toggleable_command
  2736. def topic_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2737. if len(params) < 2:
  2738. raise MissingParams
  2739. user = bot.check_for_master(user_executed)
  2740. x = 0
  2741. while x < len(params)-1:
  2742. topic = bot.find_topic(params[x].lower())
  2743. try:
  2744. level = int(params[x+1])
  2745. except ValueError:
  2746. bot.send_and_log(recipient, user_executed,
  2747. "Sorry, but that syntax is incorrect. Correct syntax: %r" % "topic add <topic> <level>...")
  2748. if level not in topic.descriptions:
  2749. bot.send_and_log(recipient, user_executed,
  2750. "Topic %r doesn't have level %d." % (topic.name, level))
  2751. else:
  2752. user.topics[topic] = level
  2753. x = x + 2
  2754. bot.send_and_log(recipient, user_executed, "Topic(s) added.")
  2755. bot.update_rules()
  2756. bot.changed()
  2757. @command("Removes one or more trigger topic(s) for you.\n"
  2758. "topic remove <topic>")
  2759. @protected_command
  2760. @toggleable_command
  2761. def topic_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2762. user = bot.check_for_master(user_executed)
  2763. for x in range(0, len(params)):
  2764. topic = bot.find_topic(params[x].lower())
  2765. try:
  2766. del user.topics[topic]
  2767. except KeyError:
  2768. bot.send_and_log(recipient, user_executed, "Could not find topic %r" % params[x].lower())
  2769. bot.send_and_log(recipient, user_executed, "Topic(s) removed.")
  2770. bot.update_rules()
  2771. bot.changed()
  2772. @command("Lists trigger topics.\n"
  2773. "topic list - Lists your own triggers.\n"
  2774. "topic list <nick> - Lists nick's triggers.")
  2775. @toggleable_command
  2776. def topic_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2777. user = bot.check_for_master(user_executed)
  2778. queried_user = bot.find_user(params[0]) if len(params) > 0 else user
  2779. if queried_user.topics:
  2780. bot.send_and_log(recipient, user_executed,
  2781. "%r has the following trigger topics set: %s."
  2782. % (queried_user.nick,
  2783. join_and(", ", " and ",
  2784. ("%s (level %d)" % (topic.name, level)
  2785. for topic, level in queried_user.topics.iteritems()))))
  2786. else:
  2787. bot.send_and_log(recipient, user_executed,
  2788. "%r has no trigger topics set." % (queried_user if len(params) > 0 else user_executed))
  2789. @command("Shows information about trigger topics.\n"
  2790. "topic info - Lists all the available trigger topics.\n"
  2791. "topic info <topic> - Shows information about a particular trigger topic.")
  2792. @toggleable_command
  2793. def topic_info(bot, params, user, recipient, mainchannel, bypass=False):
  2794. if len(params) > 0:
  2795. # Show info.
  2796. topic = bot.find_topic(params[0])
  2797. for level, description in sorted \
  2798. (topic.descriptions.iteritems(), key=lambda x:x[0]) \
  2799. :
  2800. bot.send_and_log(recipient, user,
  2801. "%s (level %d) - %s." % (topic.name, level, description))
  2802. else:
  2803. # List all.
  2804. if bot.topics:
  2805. bot.send_and_log(recipient, user,
  2806. "The following trigger topics are available: %s."
  2807. % join_and(", ", " and ",
  2808. (topic.name for topic in bot.topics.itervalues())))
  2809. else:
  2810. bot.send_and_log(recipient, user, "There are no trigger topics available. Please contact a bot admin and ask them to add some.")
  2811. @command("Take a tutorial.\n"
  2812. "tutorial [<step>]")
  2813. @toggleable_command
  2814. def tutorial(bot, params, user, recipient, mainchannel, bypass=False):
  2815. if params:
  2816. try:
  2817. if int(params[0]) == 1:
  2818. lines = ["%s allows you to define your triggers by defining trigger topics and trigger words. We will start off by explaining how to use trigger topics." % bot.nickname] \
  2819. + ["The trigger topic system allows you to set one or more trigger topics, that is, one or more topics you are sensitive to and which can trigger you."] \
  2820. + ["To get a list of trigger topics you can choose from, use the 'topic info' command. To get more information about a certain trigger topic, use the 'topic info' command, followed by a topic."] \
  2821. + ["For example, if there would be a topic named 'example', you could get more info about the levels of this topic by typing 'topic info example'."] \
  2822. + ["The list which is shown will list a level and a description. Please note that a higher level means a higher sensitivity to a specific topic."] \
  2823. + ["Please try to get used to the 'topic info' command. If you feel that you are ready to add actual trigger topics to your profile, please type 'tutorial 2' to continue."]
  2824. for line in lines:
  2825. bot.send_and_log(user, user, line)
  2826. elif int(params[0]) == 2:
  2827. lines = ["Good, time for part 2!"] \
  2828. + ["Adding a trigger topic to your profile is fairly easy. First, use 'trigger info' to decide which topic and which level you want to add."] \
  2829. + ["Now, say you want to add level 2 of 'example' to your personal trigger topic list. All you have to do is type 'topic add example 2'."] \
  2830. + ["The 'topic add' command is also used to change the level of a trigger topic already in your list. If you, for example, want to change your sensitivity level for the topic 'example' to 1, you would type 'topic add example 1'."] \
  2831. + ["Getting rid of a trigger topic is possible using the 'topic remove' command. If you feel strong enough to take discussions about 'example', just type 'topic remove example'."] \
  2832. + ["You can have as many trigger topics in your personal list as you want, so do not feel afraid to add as much as you feel is necessary."] \
  2833. + ["Whenever you join a channel, the rules will be updated to disallow discussions about subjects in your trigger topic list."] \
  2834. + ["If you think you are ready to learn about trigger words, please type 'tutorial 3'."]
  2835. for line in lines:
  2836. bot.send_and_log(user, user, line)
  2837. elif int(params[0]) == 3:
  2838. lines = ["You're a fast learner, cool!"] \
  2839. + ["Trigger words are words which you know make you feel uncomfortable and have a tendency to trigger you."] \
  2840. + ["Adding and removing trigger words goes similar to adding and removing a trigger topic, with the exceptions that trigger words have no level and can be any word you want. There is no list limiting your choices."] \
  2841. + ["If, for example, the word 'meow' makes you feel uncomfortable, you can add it to your personal trigger word list by typing 'word add meow'."] \
  2842. + ["Please note that you do not have to add variations of a trigger word, as %s contains support for so-called 'stemming', which means that it will recognise words suchs as 'meowing' to be the same as 'meow'." % bot.nickname] \
  2843. + ["To remove a trigger word from your list, type 'word remove', followed by the word you want to remove."] \
  2844. + ["It is possible to add or remove multiple trigger words from your list at once. In case you would want to add 'meow', 'woof' and 'bark', you would type 'word add meow woof bark'."] \
  2845. + ["Trigger words are useful because they will warn someone if they use a word that may trigger you, teaching them to avoid the usage of certain words."] \
  2846. + ["If you are ready to learn about account grouping, please type 'tutorial 4'."]
  2847. for line in lines:
  2848. bot.send_and_log(user, user, line)
  2849. elif int(params[0]) == 4:
  2850. lines = ["In our IRC life, we often use more than one nickname. It would be quite inconvenient if we would have to redo everything for every nickname we use. Therefore, %s supports a mechanism we call 'grouping'." % bot.nickname] \
  2851. + ["Grouping, as you may expect, groups one or more nicknames together. The idea behind grouping is that you choose a 'main' nickname, and group your alternative nicknames with the main one."] \
  2852. + ["To group, switch to one of your alternative nicknames and type 'group add', followed by your 'main' nickname. For example, if your main nickname is cutefox, you would type 'group add cutefox' using all of your alternative nicknames."] \
  2853. + ["After nicknames are grouped, you may execute a %s command from any of your nicknames, and they will be updated for all of your nicknames." % bot.nickname] \
  2854. + ["To remove an alternative nickname from a group, switch to this nickname and run 'group remove'. Please note that, if you run 'group remove' from your main nickname, %s will ungroup all your alternative nicknames as well." % bot.nickname] \
  2855. + ["Please note that, after ungrouping a nickname returns to the state it was in before it was grouped."] \
  2856. + ["You're making great progress with the tutorial. If you're ready to learn about the friend list system, please type 'tutorial 5'."]
  2857. for line in lines:
  2858. bot.send_and_log(user, user, line)
  2859. elif int(params[0]) == 5:
  2860. lines = ["Sometimes, we end up feeling quite terrible. Either we end up panicky, or just quite sad. For that case, we have the 'panic' function. The panic function, when executed, will search for someone who can help you."] \
  2861. + ["However, most of us would have priorities. We trust some people more than others, and we would like to be helped by those we trust, whenever possible. For that, we have the friend list feature."] \
  2862. + ["Whenever you use the panic feature, %s will first look for people on your friend list and if they are unavailable, look for futher help."] \
  2863. + ["To add someone to your friend list, type 'friend add', followed by the nickname of this person. Removing a friend is similar, just type 'friend remove', followed by the nickname of this person."] \
  2864. + ["If you're ready to learn about checking profile information, please type 'tutorial 6'."]
  2865. for line in lines:
  2866. bot.send_and_log(user, user, line)
  2867. elif int(params[0]) == 6:
  2868. lines = ["With all this modifying of our profiles, we sometimes get confused. That's okay, though, because checking profile information is generally very easy."] \
  2869. + ["First, we most think of what we want to get info about. Trigger topics? Trigger words? Group info? Friend info?"] \
  2870. + ["Once we've decided what we want to check, we use the command for that group, followed by the word 'list'."] \
  2871. + ["For example, to check what trigger topics you have, you type 'topic list'. 'word list', 'group list' and 'friend list' are similar examples."] \
  2872. + ["You can also get the information about someone else. In that case, just type the command followed by their nickname. As an example, to get cutefox's friendlist, you would type 'friend list cutefox'."] \
  2873. + ["If you're ready to learn about profile settings, please type 'tutorial 7."]
  2874. for line in lines:
  2875. bot.send_and_log(user, user, line)
  2876. elif int(params[0]) == 7:
  2877. lines = ["Each profile has settings. Most settings can be turned on and off, and others take a specific value."] \
  2878. + ["To get a list of settings you can set, use the 'set' command."] \
  2879. + ["To enable a setting, type 'set', followed by the setting name. For some settings, you can set a specific value. For example, to set your password to cookies, type 'set password cookies'."] \
  2880. + ["To disable a setting, type 'unset', followed by the setting name."] \
  2881. + ["To check if an option is set or unset, type 'status', followed by the setting names."] \
  2882. + ["Congratulations, this concludes the tutorial."] \
  2883. + ["To learn more about specific commands, use the 'help' command. Feel free to ask people for help if you get stuck!"] \
  2884. + ["Thank you for taking the time to go through this tutorial. I wish you a trigger-free time!"]
  2885. for line in lines:
  2886. bot.send_and_log(user, user, line)
  2887. else:
  2888. bot.send_and_log(recipient, user,
  2889. "I'm sorry, but I could not find a tutorial for that number. Currently, the tutorial has 7 steps.")
  2890. except ValueError:
  2891. bot.send_and_log(recipient, user,
  2892. "I'm sorry, but you need to enter a number.")
  2893. else:
  2894. lines = ["Welcome to the tutorial. This tutorial will teach you everything you need to know to use %s properly." % bot.nickname] \
  2895. + ["To start learning more about %s, type 'tutorial 1'." % bot.nickname]
  2896. for line in lines:
  2897. bot.send_and_log(user, user, line)
  2898. @command("Manage your trust list.\n"
  2899. "People on your trust list have complete control over your account.")
  2900. @toggleable_command
  2901. def trust(bot, params, user, recipient, mainchannel, bypass=False):
  2902. raise BadCommand
  2903. @command("Add one or more user(s) to your trust list."
  2904. "trust add <user>")
  2905. @protected_command
  2906. @toggleable_command
  2907. def trust_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2908. if params:
  2909. user = bot.check_for_master(user_executed)
  2910. for entry in params:
  2911. if entry not in user.trusts and entry != user_executed.nick and entry != user.nick and entry not in user.alts:
  2912. user.trusts.append(entry)
  2913. bot.send_and_log(recipient, user_executed,
  2914. "The requested users were added to your trust list.")
  2915. bot.changed()
  2916. else:
  2917. raise MissingParams
  2918. @command("Remove one or more user(s) from your trust list."
  2919. "trust remove <user>")
  2920. @protected_command
  2921. @toggleable_command
  2922. def trust_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2923. if params:
  2924. user = bot.check_for_master(user_executed)
  2925. for entry in params:
  2926. if entry in user.trusts:
  2927. user.trusts.remove(entry)
  2928. bot.send_and_log(recipient, user_executed,
  2929. "The requested users are no longer on your trust list.")
  2930. bot.changed()
  2931. else:
  2932. raise MissingParams
  2933. @command("List your trust list."
  2934. "trust list")
  2935. @protected_command
  2936. @toggleable_command
  2937. def trust_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2938. userdata = bot.check_for_master(user_executed)
  2939. if userdata.trusts:
  2940. bot.send_and_log(recipient, user_executed,
  2941. "You trust the following users: %s."
  2942. % join_and(", ", " and ", userdata.trusts))
  2943. else:
  2944. bot.send_and_log(recipient, user_executed,
  2945. "You don't have anyone on your trust list.")
  2946. @command("Execute commands as another user who has you on their trust list.\n"
  2947. "user <nickname> <command>")
  2948. @protected_command
  2949. @logged_command
  2950. @toggleable_command
  2951. def user(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2952. user = bot.find_user(params[0])
  2953. if bot.check_for_master(user_executed) in user.trusts:
  2954. # FIXME: Reply in-channel when executed in-channel
  2955. bot.dispatch(command=' '.join(params[1:]), user=user, reply_to=user_executed, bypass=True)
  2956. else:
  2957. bot.send_and_log(recipient, user_executed, "%s does not have you on their trust list." % user.nick)
  2958. @command("Retrieve information about an user.\n"
  2959. "whois <nick>")
  2960. @toggleable_command
  2961. def whois(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2962. if not params:
  2963. raise MissingParams
  2964. user = bot.find_user(params[0])
  2965. master = bot.check_for_master(user)
  2966. bot.send_and_log(recipient, user_executed, "%s is a %s user" % (user.nick, bot.nickname))
  2967. if user != master:
  2968. bot.send_and_log(recipient, user_executed, "%s is an alt of %s" % (user.nick, master.nick))
  2969. elif user.alts:
  2970. bot.send_and_log(recipient, user_executed, "%s is also known as %s" % (user.nick, join_and(", ", " and ", user.alts)))
  2971. if master.admin:
  2972. bot.send_and_log(recipient, user_executed, "%s is %s administrator" % (user.nick, "the head" if master.admin == 1 else "an"))
  2973. channeladmin = []
  2974. for channel in bot.channels.itervalues():
  2975. if master.nick in channel.admins:
  2976. channeladmin.append(channel)
  2977. if channeladmin:
  2978. bot.send_and_log(recipient, user_executed, "%s is a channel administrator on %s" % (user.nick, join_and(", ", " and ", channeladmin)))
  2979. @command("Deletes all your trigger topics and trigger words.\n"
  2980. "wipe")
  2981. @protected_command
  2982. @toggleable_command
  2983. def wipe(bot, params, user_executed, recipient, mainchannel, bypass=False):
  2984. user = bot.check_for_master(user_executed)
  2985. user.trigger_words.clear()
  2986. user.topics.clear()
  2987. bot.changed()
  2988. bot.send_and_log(recipient, user_executed,
  2989. "All your data are gone. I hope it's what you wanted.")
  2990. bot.update_rules()
  2991. @command("Manage words which can trigger you.")
  2992. @toggleable_command
  2993. def word(bot, params, user, recipient, mainchannel, bypass=False):
  2994. raise BadCommand
  2995. @command("Adds one or more trigger word(s) for you. Users will be warned when they use"
  2996. " this word while you are present.\n"
  2997. "word add <word>")
  2998. @protected_command
  2999. @toggleable_command
  3000. def word_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3001. if len(params) > 0:
  3002. user = bot.check_for_master(user_executed)
  3003. for entry in params:
  3004. user.trigger_words.add(bot.stem(entry.lower()))
  3005. bot.changed()
  3006. bot.send_and_log(recipient, user_executed,
  3007. "Trigger word(s) added.")
  3008. else:
  3009. raise MissingParams
  3010. @command("Removes one or more of your trigger words.\n"
  3011. "word remove <word>")
  3012. @protected_command
  3013. @toggleable_command
  3014. def word_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3015. if len(params) > 0:
  3016. user = bot.check_for_master(user_executed)
  3017. for entry in params:
  3018. user.trigger_words.discard(bot.stem(entry.lower()))
  3019. bot.changed()
  3020. bot.send_and_log(recipient, user_executed,
  3021. "Trigger word(s) removed.")
  3022. else:
  3023. raise MissingParams
  3024. @command("Lists trigger words.\n"
  3025. "word list - Lists your own trigger words.\n"
  3026. "word list <nick> - Lists nick's trigger words.")
  3027. @toggleable_command
  3028. def word_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3029. user = bot.check_for_master(user_executed)
  3030. queried_user = bot.find_user(params[0]) if params else user
  3031. if queried_user.trigger_words:
  3032. bot.send_and_log(recipient, user_executed,
  3033. "%s has the following trigger words set: %s."
  3034. % (params[0] if params else user_executed.nick,
  3035. join_and(", ", " and ", queried_user.trigger_words)))
  3036. else:
  3037. bot.send_and_log(recipient, user_executed,
  3038. "%s has no trigger words set." % (params[0] if params else user))
  3039. @command("Shows who has the given trigger word set.\n"
  3040. "word who <word>")
  3041. @toggleable_command
  3042. def word_who(bot, params, user, recipient, mainchannel, bypass=False):
  3043. word = bot.stem(params[0])
  3044. users = set()
  3045. for channel in bot.channels.itervalues():
  3046. for u in channel.users:
  3047. if word in u.trigger_words:
  3048. users.add(u)
  3049. if users:
  3050. bot.send_and_log(recipient, user,
  3051. "The following users have that trigger word: %s."
  3052. % join_and(", ", " and ", (u.nick for u in users if not u.master)))
  3053. else:
  3054. bot.send_and_log(recipient, user,
  3055. "Nobody present seems to have that trigger word.")
  3056. # Start of administrative commands
  3057. @command("Various administrative commands.")
  3058. def admin(bot, params, user, recipient, mainchannel, bypass=False):
  3059. raise BadCommand
  3060. @command("Add one or more users as administrator (admin).\n"
  3061. "admin add <user>")
  3062. @admin_command
  3063. @protected_command
  3064. @logged_command
  3065. def admin_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3066. if params:
  3067. for nick in params:
  3068. user = bot.find_user(nick)
  3069. user.admin = 2
  3070. bot.send_and_log(recipient, user_executed,
  3071. "Requested user(s) now has administrator status.")
  3072. else:
  3073. raise MissingParams
  3074. @command("Remove the administrator status from one or more users.\n"
  3075. "admin remove <user>")
  3076. @admin_command
  3077. @protected_command
  3078. @logged_command
  3079. def admin_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3080. if params:
  3081. for nick in params:
  3082. user = bot.find_user(nick)
  3083. if not user.admin == 1 or user == user_executed: # Only main admin can get rid of the main admin
  3084. user.admin = 0
  3085. else:
  3086. bot.send_and_log(recipient, user_executed,
  3087. "Could not remove admin status from %s: The main admin can only resign, not have their power taken away" % nick)
  3088. return
  3089. bot.send_and_log(recipient, user_executed,
  3090. "Requested user(s) no longer have administrator status.")
  3091. else:
  3092. raise MissingParams
  3093. @command("Sent an announcement to all users.\n"
  3094. "admin announce <message>")
  3095. @admin_command
  3096. @protected_command
  3097. @logged_command
  3098. def admin_announce(bot, params, user, recipient, mainchannel, bypass=False):
  3099. if params:
  3100. for channel in bot.channels.itervalues():
  3101. if is_channel_name(str(channel)):
  3102. bot.notice(channel,
  3103. "Announcement from %s: %s" %
  3104. (user.nick, " ".join(params)))
  3105. else:
  3106. raise MissingParams
  3107. @command("Join, leave or list managed channels.\n"
  3108. "admin channel")
  3109. def admin_channel(bot, params, user, recipient, mainchannel, bypass=False):
  3110. raise BadCommand
  3111. @command("Manage channel admins.\n"
  3112. "admin channel admin")
  3113. def admin_channel_admin(bot, params, user, recipient, mainchannel, bypass=False):
  3114. raise BadCommand
  3115. @command("Add a channel admin.\n"
  3116. "admin channel admin add <channel(s)> <user(s)>")
  3117. def admin_channel_admin_add(bot, params, user, recipient, mainchannel, bypass=False):
  3118. channels = []
  3119. for number, entry in enumerate(params):
  3120. if not is_channel_name(entry):
  3121. break
  3122. channels.append(entry)
  3123. for entry in params[number:]:
  3124. # Ensure the user is known
  3125. bot.find_user(entry)
  3126. for channel in channels:
  3127. channeldata = bot.get_channel(channel)
  3128. if not entry in channeldata.admins:
  3129. channeldata.admins.append(entry)
  3130. bot.send_and_log(recipient, user,
  3131. "Added the requested user(s) as channel admin for the requested channel(s).")
  3132. bot.changed()
  3133. @command("Remove a channel admin.\n"
  3134. "admin channel admin remove <channel(s)> <user(s)>")
  3135. def admin_channel_admin_remove(bot, params, user, recipient, mainchannel, bypass=False):
  3136. channels = []
  3137. for number, entry in enumerate(params):
  3138. if not is_channel_name(entry):
  3139. break
  3140. channels.append(entry)
  3141. for entry in params[number:]:
  3142. for channel in channels:
  3143. channeldata = bot.get_channel(channel)
  3144. if entry in channeldata.admins:
  3145. channeldata.admins.remove(entry)
  3146. bot.send_and_log(recipient, user,
  3147. "Removed the requested user(s) as channel admin for the requested channel(s).")
  3148. bot.changed()
  3149. @command("Order the bot to join one or more channel(s).\n"
  3150. "admin channel join <channel(s)>")
  3151. @admin_command
  3152. @protected_command
  3153. @logged_command
  3154. def admin_channel_join(bot, params, user, recipient, mainchannel, bypass=False):
  3155. for entry in params:
  3156. bot.join_channel(entry)
  3157. bot.send_and_log(recipient, user,
  3158. "Joined the requested channel(s).")
  3159. @command("Order the bot to leave one or more channel(s).\n"
  3160. "admin channel leave <channel(s)>")
  3161. @admin_command
  3162. @protected_command
  3163. @logged_command
  3164. def admin_channel_leave(bot, params, user, recipient, mainchannel, bypass=False):
  3165. for entry in params:
  3166. bot.leave_channel(entry, unregister=True)
  3167. bot.send_and_log(recipient, user,
  3168. "Left the requested channel(s).")
  3169. @command("Get a list of main channels the bot is managing.\n"
  3170. "admin channel list")
  3171. @admin_command
  3172. @protected_command
  3173. @logged_command
  3174. def admin_channel_list(bot, params, user, recipient, mainchannel, bypass=False):
  3175. channellist = []
  3176. for channel in bot.channels:
  3177. if is_channel_name(channel) and not "_" in channel:
  3178. channellist.append(channel)
  3179. bot.send_and_log(recipient, user,
  3180. "I'm managing %s" % join_and(", ", " and ", channellist))
  3181. @command("Create a database entry for one or more user(s).\n"
  3182. "admin create <nick>")
  3183. @admin_command
  3184. @protected_command
  3185. @logged_command
  3186. def admin_create(bot, params, user, recipient, mainchannel, bypass=False):
  3187. if not params:
  3188. raise MissingParams
  3189. for param in params:
  3190. bot.get_user(param)
  3191. bot.send_and_log(recipient, user,
  3192. "%s created" % ("Entries" if len(params) > 1 else "Entry"))
  3193. @command("Check for issues and force consistency.")
  3194. def admin_check(bot, params, user, recipient, mainchannel, bypass=False):
  3195. raise BadCommand
  3196. @command("Check and force database consistency.\n"
  3197. "admin check database")
  3198. @admin_command
  3199. @protected_command
  3200. @logged_command
  3201. def admin_check_database(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3202. bot.check_database(recipient, user_executed)
  3203. bot.send_and_log(recipient, user_executed,
  3204. "Check complete.")
  3205. @command("Export the database as triggerbot commands.\n"
  3206. "admin export <all|topics|users> [<filename>]")
  3207. def admin_export(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3208. raise BadCommand
  3209. @command("Export everything in the database as triggerbot commands.\n"
  3210. "admin export all [<filename>]")
  3211. @admin_command
  3212. @protected_command
  3213. @logged_command
  3214. def admin_export_all(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3215. if params:
  3216. filename = ' '.join(params)
  3217. else:
  3218. filename = "triggerbot_export"
  3219. bot.dispatch(command="admin export topics %s_topics" % filename, user=user_executed, reply_to=recipient, bypass=True)
  3220. bot.dispatch(command="admin export users %s_users" % filename, user=user_executed, reply_to=recipient, bypass=True)
  3221. bot.send_and_log(recipient, user_executed, "Export complete.")
  3222. @command("Export all topics as triggerbot commands.\n"
  3223. "admin export topics [<filename>]")
  3224. @admin_command
  3225. @protected_command
  3226. @logged_command
  3227. def admin_export_topics(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3228. if params:
  3229. filename = ' '.join(params)
  3230. else:
  3231. filename = "triggerbot_export_topics"
  3232. export = []
  3233. for topic in bot.topics.itervalues():
  3234. for level in topic.descriptions.keys():
  3235. export.append("!admin topic add %s %s %s" % (topic.name, level, topic.descriptions[level]))
  3236. for level in topic.words.keys():
  3237. export.append("!admin topic word add %s %s %s" % (topic.name, level, ' '.join(topic.words[level])))
  3238. for supersede in topic.supersedes:
  3239. export.append("!admin topic supersede add %s %s" % (topic.name, supersede))
  3240. with open(filename, "w") as f:
  3241. f.write('\n'.join(export))
  3242. bot.send_and_log(recipient, user_executed, "All topics have been exported.")
  3243. @command("Export all users as triggerbot commands.\n"
  3244. "For security reasons, passwords cannot be exported, as they're saved in a hashed form."
  3245. "admin export users [<filename>]")
  3246. @admin_command
  3247. @protected_command
  3248. @logged_command
  3249. def admin_export_users(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3250. if params:
  3251. filename = ' '.join(params)
  3252. else:
  3253. filename = "triggerbot_export_users"
  3254. export = []
  3255. createdusers = []
  3256. for user in bot.users.itervalues():
  3257. if user.nick not in createdusers:
  3258. export.append("!admin create user %s" % user.nick)
  3259. createdusers.append(user.nick)
  3260. for topic in user.topics.keys():
  3261. export.append("!admin user %s topic add %s %s" % (user.nick, topic.name, user.topics[topic]))
  3262. if user.trigger_words:
  3263. export.append("!admin user %s word add %s" % (user.nick, ' '.join(user.trigger_words)))
  3264. if user.admin:
  3265. export.append("!admin add %s" % user.nick)
  3266. if user.master:
  3267. if user.master not in createdusers:
  3268. export.append("!admin create user %s" % user.master)
  3269. createdusers.append(user.master)
  3270. export.append("!admin user %s group add %s" % (user.nick, user.master))
  3271. for ignore in user.ignore:
  3272. if ignore not in createdusers:
  3273. export.append("!admin create user %s" % ignore)
  3274. createdusers.append(ignore)
  3275. export.append("!admin user %s ignore add %s" % (user.nick, ignore))
  3276. if user.friends:
  3277. export.append("!admin user %s friend add %s" % (user.nick, ' '.join(user.friends)))
  3278. if user.channelallow:
  3279. export.append("!admin user %s channelallow add %s" % (user.nick, ' '.join(user.channelallow)))
  3280. for setting in ['listenmode', 'channel', 'awaycheck', 'autologout', 'autosilence', 'hideown', 'motdread']:
  3281. if getattr(user, setting) == getattr(User(None), setting):
  3282. continue
  3283. export.append("!admin user %s %s %s" % (user.nick, "set" if getattr(user, setting) else "unset", setting))
  3284. with open(filename, "w") as f:
  3285. f.write('\n'.join(export))
  3286. bot.send_and_log(recipient, user_executed, "All users have been exported.")
  3287. @command("Manage the bot's ignore list.")
  3288. @admin_command
  3289. def admin_ignore(bot, params, user, recipient, mainchannel, bypass=False):
  3290. raise BadCommand
  3291. @command("Add one or more users to the bot's ignore list, causing it to ignore all the user's commands.\n"
  3292. "admin ignore add <user>")
  3293. @admin_command
  3294. @protected_command
  3295. @logged_command
  3296. def admin_ignore_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3297. if len(params) > 0:
  3298. for nick in params:
  3299. user = bot.find_user(nick)
  3300. user.ignored = True
  3301. bot.send_and_log(recipient, user_executed,
  3302. "Requested user(s) added to the bot's ignore list.")
  3303. else:
  3304. raise MissingParams
  3305. @command("Remove one or more users from the bot's ignore list, causing it to act on their commands again.\n"
  3306. "admin ignore remove <user>")
  3307. @admin_command
  3308. @protected_command
  3309. @logged_command
  3310. def admin_ignore_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3311. if len(params) > 0:
  3312. for nick in params:
  3313. user = bot.find_user(nick)
  3314. user.ignored = False
  3315. bot.send_and_log(recipient, user_executed,
  3316. "Requested user(s) removed from the bot's ignore list.")
  3317. else:
  3318. raise MissingParams
  3319. @command("List users on the bot's ignore list.")
  3320. @admin_command
  3321. @protected_command
  3322. def admin_ignore_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3323. ignoredusers = []
  3324. for user in bot.users.itervalues():
  3325. if user.ignored:
  3326. ignoredusers.append(user.nick)
  3327. if len(ignoredusers) > 0:
  3328. bot.send_and_log(recipient, user_executed,
  3329. "The following users are on the bot's ignore list: %s." %
  3330. ', '.join(ignoredusers))
  3331. else:
  3332. bot.send_and_log(recipient, user_executed,
  3333. "The bot's ignore list is empty.")
  3334. @command("Order the bot to kick someone out.\n"
  3335. "When listing channels, make sure they all start with a channel character. The first value that does not is seen as the user value.\n"
  3336. "admin kick [<channels>] <user> [<reason>]")
  3337. @admin_command
  3338. @protected_command
  3339. @logged_command
  3340. def admin_kick(bot, params, user, recipient, mainchannel, bypass=False):
  3341. if not params:
  3342. raise MissingParams
  3343. channels = []
  3344. for param in params:
  3345. if is_channel_name(param):
  3346. channels.append(param.split("_")[0])
  3347. if not len(params) > len(channels):
  3348. raise MissingParams
  3349. for channel in bot.channels.itervalues():
  3350. if channels:
  3351. for kickfromchannel in channels:
  3352. if str(channel) == kickfromchannel or str(channel).startswith("%s_" % kickfromchannel):
  3353. bot.kick(str(channel), params[len(channels)], "%s: %s" % (user, params[len(channels)+1:] if len(params) >= len(channels)+2 else "no reason specified."))
  3354. else:
  3355. bot.kick(str(channel), params[len(channels)], "%s: %s" % (user, params[len(channels)+1:] if len(params) >= len(channels)+2 else "no reason specified."))
  3356. @command("Order the bot to ban an user.\n"
  3357. "When listing channels, make sure they all start with a channel character. The first value that does not is seen as the user value.\n"
  3358. "admin ban [<channels>] <user>")
  3359. @admin_command
  3360. @protected_command
  3361. @logged_command
  3362. def admin_ban(bot, params, user, recipient, mainchannel, bypass=False):
  3363. if not params:
  3364. raise MissingParams
  3365. channels = []
  3366. for param in params:
  3367. if is_channel_name(param):
  3368. channels.append(param.split("_")[0])
  3369. if not len(params) > len(channels):
  3370. raise MissingParams
  3371. toban = bot.get_user(params[len(channels)])
  3372. for channel in bot.channels.itervalues():
  3373. if channels:
  3374. for banfromchannel in channels:
  3375. if str(channel) == banfromchannel or str(channel).startswith("%s_" % banfromchannel):
  3376. bot.mode(chan=str(channel), set=True, modes="b", mask="*!*@%s" % toban.host)
  3377. else:
  3378. bot.mode(chan=str(channel), set=True, modes="b", mask="*!*@%s" % toban.host)
  3379. @command("Remove the ban on an user.\n"
  3380. "When listing channels, make sure they all start with a channel character. The first value that does not is seen as the user value.\n"
  3381. "admin unban [<channels>] <user>")
  3382. @admin_command
  3383. @protected_command
  3384. @logged_command
  3385. def admin_unban(bot, params, user, recipient, mainchannel, bypass=False):
  3386. if not params:
  3387. raise MissingParams
  3388. channels = []
  3389. for param in params:
  3390. if is_channel_name(param):
  3391. channels.append(param.split("_")[0])
  3392. if not len(params) > len(channels):
  3393. raise MissingParams
  3394. tounban = bot.get_user(params[len(channels)])
  3395. for channel in bot.channels.itervalues():
  3396. if channels:
  3397. for banfromchannel in channels:
  3398. if str(channel) == banfromchannel or str(channel).startswith("%s_" % banfromchannel):
  3399. bot.mode(chan=str(channel), set=False, modes="b", mask="*!*@%s" % tounban.host)
  3400. else:
  3401. bot.mode(chan=str(channel), set=False, modes="b", mask="*!*@%s" % tounban.host)
  3402. @command("Order the bot to kickban an user.\n"
  3403. "When listing channels, make sure they all start with a channel character. The first value that does not is seen as the user value.\n"
  3404. "admin kickban [<channels>] <user>")
  3405. @admin_command
  3406. @protected_command
  3407. @logged_command
  3408. def admin_kickban(bot, params, user, recipient, mainchannel, bypass=False):
  3409. bot.dispatch(command="admin kick %s" % ' '.join(params), user=user, reply_to=recipient, bypass=True)
  3410. bot.dispatch(command="admin ban %s" % ' '.join(params), user=user, reply_to=recipient, bypass=True)
  3411. @command("View admin logs.\n"
  3412. "By default, the 10 latest entries are displayed. Type a number or 'all' to see more entries.\n"
  3413. "admin logs [<user>] [<entries>]")
  3414. @admin_command
  3415. @protected_command
  3416. def admin_logs(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3417. start = -10
  3418. tosend = []
  3419. channellength = 7
  3420. timelength = 4
  3421. userlength = 4
  3422. users = []
  3423. if params:
  3424. for param in params:
  3425. try:
  3426. users.append(bot.find_user(param))
  3427. except UserNotFound:
  3428. if param == "all":
  3429. start = 0
  3430. else:
  3431. try:
  3432. start = -int(param)
  3433. except ValueError:
  3434. raise UserNotFound(param)
  3435. if not users:
  3436. users = bot.users.itervalues()
  3437. logs = {}
  3438. for user in users:
  3439. for log in user.logs.keys():
  3440. logs[log] = user.nick, user.logs[log]
  3441. if not logs:
  3442. bot.send_and_log(recipient, user_executed,
  3443. "No logs were found for %s" % join_and(", ", " and ", users))
  3444. return
  3445. if start < 0 and -start < len(logs):
  3446. bot.send_and_log(recipient, user_executed,
  3447. "Limiting output to the most recent %s of %s entries." % (-start, len(logs)))
  3448. else:
  3449. bot.send_and_log(recipient, user_executed,
  3450. "Showing all %s entries." % len(logs))
  3451. sorted_logs = sorted(logs.keys())[start:]
  3452. for log in sorted_logs:
  3453. # Channel | Time | User | Command
  3454. channel = str(logs[log][1][0])
  3455. time = log.strftime("%Y-%m-%d %H:%M:%S")
  3456. user = logs[log][0]
  3457. command = logs[log][1][1]
  3458. tosend.append("%s | %s | %s | %s" % (channel, time, user, command))
  3459. if len(channel) > channellength:
  3460. channellength = len(channel)
  3461. if len(time) > timelength:
  3462. timelength = len(time)
  3463. if len(user) > userlength:
  3464. userlength = len(user)
  3465. bot.send_and_log(recipient, user_executed,
  3466. "%s | %s | %s | command" % ("channel".center(channellength), "time".center(timelength), "user".center(userlength)))
  3467. for abouttosend in tosend:
  3468. abouttosend = "%s | %s | %s | %s" % (abouttosend.split(" | ")[0].center(channellength), abouttosend.split(" | ")[1].center(timelength), abouttosend.split(" | ")[2].center(userlength), abouttosend.split(" | ")[3])
  3469. bot.send_and_log(recipient, user_executed, abouttosend)
  3470. @command("Manage which admin commands a specific non-admin can execute.")
  3471. def admin_permission(bot, params, user, recipient, mainchannel, bypass=False):
  3472. raise BadCommand
  3473. @command("Give an user the ability to execute a certain admin command.\n"
  3474. "admin permission add <user> <command>")
  3475. @admin_command
  3476. @protected_command
  3477. @logged_command
  3478. def admin_permission_add(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3479. if len(params) > 1:
  3480. user = bot.get_user(params[0])
  3481. if params[1] != "admin":
  3482. params.insert(1, "admin")
  3483. try:
  3484. bot.command_description(params[1:])
  3485. if not "_".join(params[1:]) in user.admincommandsallowed:
  3486. user.admincommandsallowed.append("_".join(params[1:]))
  3487. bot.send_and_log(recipient, user_executed,
  3488. "%s is now allowed to run the command %r" %
  3489. (params[0], " ".join(params[1:])))
  3490. else:
  3491. bot.send_and_log(recipient, user_executed,
  3492. "%s was already allowed to run the command %r" %
  3493. (params[0], " ".join(params[1:])))
  3494. except KeyError:
  3495. raise CommandNotFound(" ".join(params[1:]))
  3496. @command("Remove an user's ability to execute a certain admin command.\n"
  3497. "admin permission remove <user> <command>")
  3498. @admin_command
  3499. @protected_command
  3500. @logged_command
  3501. def admin_permission_remove(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3502. if len(params) > 1:
  3503. user = bot.get_user(params[0])
  3504. if params[1] != "admin":
  3505. params.insert(1, "admin")
  3506. if "_".join(params[1:]) in user.admincommandsallowed:
  3507. user.admincommandsallowed.remove("_".join(params[1:]))
  3508. bot.send_and_log(recipient, user_executed,
  3509. "%s is no longer allowed to run the command %r" %
  3510. (params[0], " ".join(params[1:])))
  3511. else:
  3512. bot.send_and_log(recipient, user_executed,
  3513. "%s was already not allowed to run the command %r" %
  3514. (params[0], " ".join(params[1:])))
  3515. @command("Disconnects and shuts the bot down.\n"
  3516. "admin quit")
  3517. @admin_command
  3518. @protected_command
  3519. @logged_command
  3520. def admin_quit(bot, params, user, recipient, mainchannel, bypass=False):
  3521. global reconnectondc
  3522. reconnectondc = False
  3523. bot.quit()
  3524. @command("Orders the bot to reconnect to the server.\n"
  3525. "admin reconnect")
  3526. @admin_command
  3527. @protected_command
  3528. @logged_command
  3529. def admin_reconnect(bot, params, user, recipient, mainchannel, bypass=False):
  3530. global reconnectondc
  3531. reconnectondc = True
  3532. bot.quit()
  3533. @command("Set a global settings")
  3534. def admin_set(bot, params, user, recipient, mainchannel, bypass=False):
  3535. raise BadCommand
  3536. @command("Unset a global setting")
  3537. def admin_unset(bot, params, user, recipient, mainchannel, bypass=False):
  3538. raise BadCommand
  3539. @command("Set the global MOTD, displayed in all channel topics.\n"
  3540. "admin set globalmotd <value>")
  3541. @admin_command
  3542. @protected_command
  3543. @logged_command
  3544. def admin_set_globalmotd(bot, params, user, recipient, mainchannel, bypass=False):
  3545. bot.get_settings().globalmotd = " ".join(params)
  3546. if params:
  3547. bot.send_and_log(recipient, user, "Global MOTD set.")
  3548. else:
  3549. bot.send_and_log(recipient, user, "Global MOTD disabled.")
  3550. for userloop in bot.users:
  3551. bot.dispatch(command="unset motdread", user=bot.get_user(userloop), reply_to=None, bypass=True)
  3552. bot.changed()
  3553. @command("Disable the main channels.\n"
  3554. "admin set maindisabled")
  3555. @admin_command
  3556. @protected_command
  3557. @logged_command
  3558. def admin_set_maindisabled(bot, params, user, recipient, mainchannel, bypass=False):
  3559. if not bot.get_settings().maindisabled:
  3560. bot.get_settings().maindisabled = True
  3561. bot.send_and_log(recipient, user, "Main channels disabled.")
  3562. else:
  3563. bot.send_and_log(recipient, user, "Main channels are already disabled.")
  3564. @command("Enable the main channels.\n"
  3565. "admin unset maindisabled")
  3566. @admin_command
  3567. @protected_command
  3568. @logged_command
  3569. def admin_unset_maindisabled(bot, params, user, recipient, mainchannel, bypass=False):
  3570. if bot.get_settings().maindisabled:
  3571. bot.get_settings().maindisabled = False
  3572. bot.send_and_log(recipient, user, "Main channels enabled.")
  3573. else:
  3574. bot.send_and_log(recipient, user, "Main channels are already enabled.")
  3575. @command("Manage which commands are enabled or disabled.")
  3576. def admin_togglecommand(bot, params, user, recipient, mainchannel, bypass=False):
  3577. raise BadCommand
  3578. # TODO: Don't require exact matching with disabled command
  3579. @command("Enable a command.\n"
  3580. "admin togglecommand enable <command>")
  3581. @admin_command
  3582. @protected_command
  3583. @logged_command
  3584. def admin_togglecommand_enable(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3585. if params:
  3586. try:
  3587. bot.command_description(params)
  3588. if "_".join(params) in bot.get_settings().disabledcommands:
  3589. bot.get_settings().disabledcommands.remove("_".join(params))
  3590. bot.send_and_log(recipient, user_executed,
  3591. "Command %r is no longer disabled." %
  3592. " ".join(params))
  3593. bot.changed()
  3594. else:
  3595. bot.send_and_log(recipient, user_executed,
  3596. "Command %r has not been disabled." %
  3597. " ".join(params))
  3598. except KeyError:
  3599. raise CommandNotFound(" ".join(params[1:]))
  3600. else:
  3601. raise MissingParams
  3602. # TODO: Give a message when the command is not toggleable
  3603. @command("Disable a command and all its subcommands.\n"
  3604. "admin togglecommand disable <command>")
  3605. @admin_command
  3606. @protected_command
  3607. @logged_command
  3608. def admin_togglecommand_disable(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3609. if params:
  3610. if not "_".join(params) in bot.get_settings().disabledcommands:
  3611. bot.get_settings().disabledcommands.append("_".join(params))
  3612. bot.send_and_log(recipient, user_executed,
  3613. "Command %r and all its subcommands are now disabled." %
  3614. " ".join(params))
  3615. bot.changed()
  3616. else:
  3617. bot.send_and_log(recipient, user_executed,
  3618. "Command %r and all its subcommands were already disabled." %
  3619. " ".join(params))
  3620. else:
  3621. raise MissingParams
  3622. # TODO: Format list a bit more nicely (e.g.: 'toggle list' instead of toggle_list)
  3623. @command("Get a list of disabled commands.\n"
  3624. "admin togglecommand list")
  3625. @admin_command
  3626. @protected_command
  3627. def admin_togglecommand_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3628. if bot.get_settings().disabledcommands:
  3629. bot.send_and_log(recipient, user_executed,
  3630. "The following commands have been disabled: %s." %
  3631. join_and(", ", " and ", bot.get_settings().disabledcommands))
  3632. else:
  3633. bot.send_and_log(recipient, user_executed,
  3634. "No commands have been disabled.")
  3635. @command("Adds and removes trigger topics.")
  3636. def admin_topic(bot, params, user, recipient, mainchannel, bypass=False):
  3637. raise BadCommand
  3638. @command("Adds a trigger topic.\n"
  3639. "admin topic add <name> <level> <description>")
  3640. @admin_command
  3641. @protected_command
  3642. @logged_command
  3643. def admin_topic_add(bot, params, user, recipient, mainchannel, bypass=False):
  3644. if len(params) > 2:
  3645. name = params[0].lower()
  3646. level = int(params[1])
  3647. description = " ".join(params[2:])
  3648. if name in bot.topics:
  3649. topic = bot.topics[name]
  3650. else:
  3651. topic = Topic(name)
  3652. bot.topics[topic.name] = topic
  3653. topic.descriptions[level] = description
  3654. bot.changed()
  3655. bot.send_and_log(recipient, user, "It is done.")
  3656. else:
  3657. raise MissingParams
  3658. @command("Manage topic detection words.\n")
  3659. def admin_topic_word(bot, params, user, recipient, mainchannel, bypass=False):
  3660. raise BadCommand
  3661. @command("Add words to a topic.\n"
  3662. "These words will be used to detect the topic a certain message is about.\n"
  3663. "admin topic word add <name> <level> <list>")
  3664. @admin_command
  3665. @protected_command
  3666. @logged_command
  3667. def admin_topic_word_add(bot, params, user, recipient, mainchannel, bypass=False):
  3668. if len(params) > 2:
  3669. topic = bot.find_topic(params[0].lower())
  3670. level = int(params[1])
  3671. if not level in topic.words.keys():
  3672. topic.words[level] = []
  3673. for word in params[2:]:
  3674. if not bot.stem(word.lower()) in topic.words[level]:
  3675. topic.words[level].append(bot.stem(word.lower()))
  3676. bot.changed()
  3677. bot.send_and_log(recipient, user, "Words added")
  3678. else:
  3679. raise MissingParams
  3680. @command("Remove words from a topic.\n"
  3681. "These words will be used to detect the topic a certain message is about.\n"
  3682. "admin topic word remove <name> <level> <list>")
  3683. @admin_command
  3684. @protected_command
  3685. @logged_command
  3686. def admin_topic_word_remove(bot, params, user, recipient, mainchannel, bypass=False):
  3687. if len(params) > 2:
  3688. topic = bot.find_topic(params[0].lower())
  3689. level = int(params[1])
  3690. for word in params[2:]:
  3691. if bot.stem(word.lower()) in topic.words[level]:
  3692. topic.words[level].remove(word.lower())
  3693. bot.changed()
  3694. bot.send_and_log(recipient, user, "Words removed")
  3695. else:
  3696. raise MissingParams
  3697. @command("List words from a topic.\n"
  3698. "admin topic word list <name>")
  3699. @admin_command
  3700. @protected_command
  3701. @logged_command
  3702. def admin_topic_word_list(bot, params, user, recipient, mainchannel, bypass=False):
  3703. if params:
  3704. topic = bot.find_topic(params[0].lower())
  3705. bot.send_and_log(recipient, user, topic.words)
  3706. else:
  3707. raise MissingParams
  3708. @command("Removes a trigger topic.\n"
  3709. "If level is not specified, removes all levels.\n"
  3710. "admin topic remove <name> [<level>]")
  3711. @admin_command
  3712. @protected_command
  3713. @logged_command
  3714. def admin_topic_remove(bot, params, user, recipient, mainchannel, bypass=False):
  3715. topic = bot.find_topic(params[0])
  3716. if len(params) >= 2:
  3717. level = int(params[1])
  3718. del topic.descriptions[level]
  3719. else:
  3720. del bot.topics[topic.name]
  3721. # Note that some users might still refer to this topic.
  3722. bot.changed()
  3723. bot.send_and_log(recipient, user, "Topic removed.")
  3724. @command("Manage topic superseding")
  3725. @admin_command
  3726. @protected_command
  3727. @logged_command
  3728. def admin_topic_supersede(bot, params, user, recipient, mainchannel, bypass=False):
  3729. raise BadCommand
  3730. @command("Add one or more topic(s) to the list of topics the specified topic supersedes.\n"
  3731. "admin topic supersede add <topic> <superseded topic>")
  3732. @admin_command
  3733. @protected_command
  3734. @logged_command
  3735. def admin_topic_supersede_add(bot, params, user, recipient, mainchannel, bypass=False):
  3736. topic = bot.find_topic(params[0])
  3737. for entry in params[1:]:
  3738. topic.supersedes.append(entry)
  3739. bot.changed()
  3740. bot.send_and_log(recipient, user, "Requested topic(s) will now be superseded by %s." % topic.name)
  3741. @command("Remove one or more topic(s) to the list of topics the specified topic supersedes.\n"
  3742. "admin topic supersede remove <topic> <superseded topic>")
  3743. @admin_command
  3744. @protected_command
  3745. @logged_command
  3746. def admin_topic_supersede_remove(bot, params, user, recipient, mainchannel, bypass=False):
  3747. topic = bot.find_topic(params[0])
  3748. for entry in params[1:]:
  3749. if entry in topic.supersedes:
  3750. topic.supersedes.remove(entry)
  3751. bot.changed()
  3752. bot.send_and_log(recipient, user, "Requested topic(s) will no longer be superseded by %s." % topic.name)
  3753. @command("List which topics are superseded by a specific topic.\n"
  3754. "admin topic supersede list <topic>")
  3755. @admin_command
  3756. @protected_command
  3757. def admin_topic_supersede_list(bot, params, user, recipient, mainchannel, bypass=False):
  3758. topic = bot.find_topic(params[0])
  3759. if len(topic.supersedes) > 0:
  3760. bot.send_and_log(recipient, user, "%s supersedes the following topics: %s"
  3761. % (topic.name, join_and(", ", " and ", (entry for entry in topic.supersedes))))
  3762. else:
  3763. bot.send_and_log(recipient, user, "%s does not supersede any topics." % topic.name)
  3764. @command("Execute commands as another user.\n"
  3765. "admin user <nickname> <command>")
  3766. @admin_command
  3767. @protected_command
  3768. @logged_command
  3769. def admin_user(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3770. user = bot.find_user(params[0])
  3771. # FIXME: Reply in-channel when executed in-channel
  3772. bot.dispatch(command=' '.join(params[1:]), user=user, reply_to=user_executed, bypass=True)
  3773. @command("Warn an user.\n"
  3774. "admin warn <user> [<reason>]")
  3775. @admin_command
  3776. @protected_command
  3777. @logged_command
  3778. def admin_warn(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3779. if len(params) > 0:
  3780. user = bot.find_user(params[0])
  3781. user.warnings[datetime.datetime.now()] = None, user_executed.nick, str(" ".join(params[1:])) if len(params) > 1 else None
  3782. warningsbytriggerbot = 0
  3783. for entry in user.warnings.keys():
  3784. if user.warnings[entry][1] == None:
  3785. warningsbytriggerbot = warningsbytriggerbot + 1
  3786. if user.warnings[entry][2:] == None:
  3787. bot.send_and_log(user, None,
  3788. "%s has sent you a warning."
  3789. % user.warnings[entry][1])
  3790. else:
  3791. bot.send_and_log(user, None,
  3792. "%s has sent you a warning with the following reason: %s."
  3793. % (user.warnings[entry][1], user.warnings[entry][2]))
  3794. bot.send_and_log(user, None,
  3795. "Please think about your behaviour and try to improve it.")
  3796. bot.send_and_log(user, None,
  3797. "For your information, you currently have %s warning(s), of which %s were automatically sent to you by %s." %
  3798. (len(user.warnings), warningsbytriggerbot, bot.nickname))
  3799. else:
  3800. raise MissingParams
  3801. @command("Manage an user's warnings.")
  3802. @admin_command
  3803. @protected_command
  3804. def admin_warnings(bot, params, user, recipient, mainchannel, bypass=False):
  3805. raise BadCommand
  3806. @command("List the amount of warnings an user has.\n"
  3807. "Use 'verbose' to receive a more verbose list.\n"
  3808. "By default, only the last 10 entries are shown in verbose mode. Type a number on 'all' to show more entries.\n"
  3809. "admin warnings list <user> [<type>] [<entries>]")
  3810. @admin_command
  3811. @protected_command
  3812. def admin_warnings_list(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3813. if len(params) > 0:
  3814. user = bot.find_user(params[0])
  3815. if len(user.warnings) > 0:
  3816. if len(params) <= 1 or params[1] != "verbose":
  3817. warningsbyuser = {}
  3818. for entry in user.warnings.keys():
  3819. warner = user.warnings[entry][1] if user.warnings[entry][1] != None else bot.nickname
  3820. if warner in warningsbyuser:
  3821. warningsbyuser[warner] = warningsbyuser[warner] + 1
  3822. else:
  3823. warningsbyuser[warner] = 1
  3824. bot.send_and_log(recipient, user_executed,
  3825. "Since %s, %s has received %s warning(s). %s." %
  3826. (user.warnings.keys()[0].strftime("%Y-%m-%d %H:%M:%S"), user.nick, len(user.warnings.keys()), ', '.join(["%s by %s" %
  3827. (warningdata[1], warningdata[0]) for warningdata in warningsbyuser.items()])))
  3828. else:
  3829. if user.warnings:
  3830. start = len(user.warnings)-10
  3831. try:
  3832. if params[2] == "all":
  3833. start = 0
  3834. else:
  3835. try:
  3836. start = len(user.warnings)-int(params[2])
  3837. except:
  3838. pass
  3839. except:
  3840. pass
  3841. if start > 0:
  3842. bot.send_and_log(recipient, user_executed,
  3843. "Limiting output to the most recent %s of %s entries." % (len(user.warnings)-start, len(user.warnings)))
  3844. else:
  3845. bot.send_and_log(recipient, user_executed,
  3846. "Showing all %s entries." % len(user.warnings))
  3847. for number, warning in enumerate(user.warnings.keys()[start:]):
  3848. bot.send_and_log(recipient, user_executed,
  3849. "%s - warned by %s: %s"
  3850. % (user.warnings.keys()[number].strftime("%Y-%m-%d %H:%M:%S"), bot.nickname if user.warnings[warning][1] == None else user.warnings[warning][1], "No reason specified." if user.warnings[warning][2] == None else user.warnings[warning][2]))
  3851. else:
  3852. bot.send_and_log(recipient, user_executed,
  3853. "%s has not received any warnings." % user)
  3854. else:
  3855. raise MissingParams
  3856. @command("Reset one or more users' warnings.\n"
  3857. "admin warnings reset <user>")
  3858. @admin_command
  3859. @protected_command
  3860. @logged_command
  3861. def admin_warnings_reset(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3862. if len(params) > 0:
  3863. for nick in params:
  3864. user = bot.find_user(nick)
  3865. user.warnings = {}
  3866. bot.send_and_log(recipient, user_executed,
  3867. "Warnings reset.")
  3868. else:
  3869. raise MissingParams
  3870. @command("Delete all someone's trigger topics and words.\n"
  3871. "admin wipe <user>")
  3872. @admin_command
  3873. @protected_command
  3874. @logged_command
  3875. def admin_wipe(bot, params, user, recipient, mainchannel, bypass=False):
  3876. target = bot.find_user(params[0])
  3877. target.trigger_words.clear()
  3878. target.topics.clear()
  3879. bot.changed()
  3880. bot.send_and_log(recipient, user,
  3881. "All data for %s are gone. I hope it's what you wanted."
  3882. % target)
  3883. @command("Claim administrative powers if no admin has been registered yet.\n"
  3884. "claimadmin")
  3885. def claimadmin(bot, params, user_executed, recipient, mainchannel, bypass=False):
  3886. for user in bot.users.itervalues():
  3887. if user.admin == 1:
  3888. bot.send_and_log(recipient, user_executed, "A head admin already exists. Therefore, you cannot claim administrative power.")
  3889. return
  3890. user_executed.admin = 1
  3891. bot.send_and_log(recipient, user_executed, "You have claimed administrative powers.")
  3892. register_commands()
  3893. class TriggerBotFactory(protocol.ReconnectingClientFactory):
  3894. """A factory for TriggerBots.
  3895. A new protocol instance will be created each time we connect to the server.
  3896. """
  3897. # This factor is an approximation of Phi, instead of Twisted's default
  3898. # approximation of e. Because triggerbot should limit its downtime, a
  3899. # lower exponential backoff value is useful
  3900. factor = 1.6180339887498948
  3901. def __init__(self, channellist, channelsdefined, logger, filename, nickname, identify, identifypassword):
  3902. self.channellist = channellist
  3903. self.channelsdefined = channelsdefined
  3904. self.logger = logger
  3905. self.filename = filename
  3906. self.nickname = nickname
  3907. self.identify = identify
  3908. self.identifypassword = identifypassword
  3909. def buildProtocol(self, addr):
  3910. p = TriggerBot()
  3911. p.channellist = self.channellist
  3912. p.channelsdefined = self.channelsdefined
  3913. p.logger = self.logger
  3914. p.filename = self.filename
  3915. p.nickname = self.nickname
  3916. p.wantednick = self.nickname
  3917. p.identify = self.identify
  3918. if self.identify == True:
  3919. p.identifypassword = self.identifypassword
  3920. p.stem = xapian.Stem("en")
  3921. self.resetDelay()
  3922. return p
  3923. def clientConnectionLost(self, connector, reason):
  3924. global reconnectondc
  3925. if reconnectondc:
  3926. connector.connect()
  3927. else:
  3928. reactor.stop()
  3929. def main():
  3930. """Main function. Called from main.py."""
  3931. # Default values:
  3932. database = "triggerbot.db"
  3933. nickname = "triggerbot"
  3934. channellist = []
  3935. logfile = None
  3936. identify = False
  3937. identifypassword = None
  3938. serverdefined = False
  3939. portdefined = False
  3940. channelsdefined = False
  3941. for index, arg in enumerate(sys.argv):
  3942. if arg == "--server" or arg == "-s":
  3943. server = sys.argv[index+1]
  3944. serverdefined = True
  3945. elif arg == "--port" or arg == "-p":
  3946. port = sys.argv[index+1]
  3947. portdefined = True
  3948. elif arg == "--channel" or arg == "-c":
  3949. channelsdefined = True
  3950. for x in range(index, len(sys.argv)):
  3951. try:
  3952. if sys.argv[x+1][:1] != "-":
  3953. channellist.append(sys.argv[x+1])
  3954. else:
  3955. break
  3956. except IndexError:
  3957. # End of list, don't worry
  3958. break
  3959. elif arg == "--nick" or arg == "-n":
  3960. nickname = sys.argv[index+1]
  3961. elif arg == "--identify" or arg == "-i":
  3962. identify = True
  3963. identifypassword = sys.argv[index+1]
  3964. elif arg == "--logfile" or arg == "-l":
  3965. logfile = sys.argv[index+1]
  3966. elif arg == "--database" or arg == "-d":
  3967. database = sys.argv[index+1]
  3968. if serverdefined != True or portdefined != True:
  3969. print "Please specify at least the server and port info using --server (-s) and --port (-p) followed by the related information."
  3970. exit(1)
  3971. log.startLogging(sys.stdout)
  3972. logger = MessageLogger \
  3973. (open(logfile, "a") if logfile != None
  3974. else sys.stdout)
  3975. global reconnectondc
  3976. reconnectondc = True
  3977. f = TriggerBotFactory \
  3978. (channellist=channellist, channelsdefined=channelsdefined, logger=logger, filename=database, nickname=nickname, identify=identify, identifypassword=identifypassword)
  3979. reactor.connectTCP(server, int(port), f)
  3980. reactor.run()