admin.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. from common import userstatus, do_everything_to
  2. from commands.common import CommandHelpers as CH
  3. from commands.common import AdminHelpers as AH
  4. bold = "\x02"
  5. italic = "\x1D"
  6. underline = "\x1F"
  7. reverse = "\x16" # swap background and foreground colors ("reverse video")
  8. reset = "\x0F"
  9. blue = "\x0302"
  10. green = "\x0303"
  11. red = "\x0304"
  12. grey = "\x0314"
  13. def do_command(self, connection, event):
  14. cmdtype, trigger, command, replyto = CH.disect_command(self, event)
  15. if not command:
  16. return # Do nothing if there is no command.
  17. # Ignore channel commands from users that do not have at least voice in homechannel or operator status in target channel.
  18. if event.type == "pubmsg": # It's a channel message.
  19. if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, event.target): # Does not have at least voiced status in homechannel or operator status in target channel.
  20. return
  21. if command == "cmd" or command == "cmds" or command == "commands":
  22. if cmdtype == "cmd":
  23. message = grey + "Admin: "
  24. if CH.ccc(self, "channelfunctions", {"homechan": "oper", "chan": "oper"}, event):
  25. message += CH.ccc(self, "channelfunctions", {"homechan": "oper", "chan": "oper"}, event)
  26. if CH.ccc(self, "join", {"homechan": "oper", "chan": None}, event):
  27. message += CH.ccc(self, "join", {"homechan": "oper", "chan": None}, event)
  28. if CH.ccc(self, "part", {"homechan": "oper", "chan": None}, event):
  29. message += CH.ccc(self, "part", {"homechan": "oper", "chan": None}, event)
  30. if CH.ccc(self, "quit", {"homechan": "admin", "chan": None}, event):
  31. message += CH.ccc(self, "quit", {"homechan": "admin", "chan": None}, event)
  32. if CH.ccc(self, "reconnect", {"homechan": "oper", "chan": None}, event):
  33. message += CH.ccc(self, "reconnect", {"homechan": "oper", "chan": None}, event)
  34. if CH.ccc(self, "recovernick", {"homechan": "oper", "chan": None}, event):
  35. message += CH.ccc(self, "recovernick", {"homechan": "oper", "chan": None}, event)
  36. if CH.ccc(self, "msg", {"homechan": "oper", "chan": "oper"}, event):
  37. message += CH.ccc(self, "msg", {"homechan": "oper", "chan": "oper"}, event)
  38. if CH.ccc(self, "act", {"homechan": "oper", "chan": "oper"}, event):
  39. message += CH.ccc(self, "act", {"homechan": "oper", "chan": "oper"}, event)
  40. if message == grey + "Admin: ": # No commands to display.
  41. return
  42. connection.privmsg(replyto, message[:-2] + ".")
  43. elif command.split()[0] == "quit":
  44. if not userstatus.atleast_admin(self, event.source.nick, self.homechannel): #Insufficient rights.
  45. connection.privmsg(replyto, "Denied, you need to have admin (super operator) status or higher in " + red + self.homechannel + reset + ".")
  46. return
  47. if cmdtype == "help": # Display help text.
  48. if len(command.split()) is not 1:
  49. return
  50. connection.privmsg(replyto, "Disconnect and terminate " + connection.get_nickname() + ". Optionally with reason.")
  51. connection.privmsg(replyto, grey + "Usage: " + blue + "!quit " + reset + italic + "reason")
  52. elif cmdtype == "cmd":
  53. if len(command.split()) == 1:
  54. self.die(msg = "Killed by " + event.source.nick)
  55. else:
  56. self.die(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
  57. elif command.split()[0] == "reconnect":
  58. if not userstatus.atleast_oper(self, event.source.nick, self.homechannel):
  59. connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel + reset + ".")
  60. return
  61. if cmdtype == "help": # Display help text.
  62. if len(command.split()) is not 1:
  63. return
  64. connection.privmsg(replyto, "Reconnect " + connection.get_nickname() + ". Reason optional.")
  65. connection.privmsg(replyto, grey + "Usage: " + blue + "!reconnect " + reset + italic + "reason")
  66. elif cmdtype == "cmd":
  67. if len(command.split()) == 1:
  68. self.disconnect(msg = "Reconnect requested by " + event.source.nick)
  69. else:
  70. self.disconnect(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
  71. elif command.split()[0] == "recovernick":
  72. if not userstatus.atleast_voiced(self, event.source.nick, self.homechannel):
  73. connection.privmsg(replyto, "Denied, you need to have voiced status or higher in " + red + self.homechannel + reset + ".")
  74. return
  75. if cmdtype == "help": # Display help text.
  76. if len(command.split()) is not 1:
  77. return
  78. connection.privmsg(replyto, "Let " + connection.get_nickname() + " try to recover " + connection.nickname + " as nickname.")
  79. elif cmdtype == "cmd":
  80. if connection.get_nickname() == connection.nickname:
  81. connection.action(replyto, "is already named " + red + connection.nickname + reset + ".")
  82. return
  83. from common.networkservices import NickServ
  84. NickServ.recover_nick(connection, self.password)
  85. elif command.split()[0] == "join":
  86. if not userstatus.atleast_oper(self, event.source.nick, self.homechannel):
  87. connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel + reset + ".")
  88. return
  89. if cmdtype == "help": #Display help text.
  90. if len(command.split()) is not 1:
  91. return
  92. connection.privmsg(replyto, "Make " + connection.get_nickname() + " join a channel. Password optional.")
  93. connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
  94. elif cmdtype == "cmd":
  95. try:
  96. channel = command.split()[1]
  97. except IndexError:
  98. connection.privmsg(replyto, "Specify channel. For help type: " + blue + self.helpchar + "join")
  99. return
  100. try:
  101. key = command.split(maxsplit=2)[2]
  102. except IndexError:
  103. do_everything_to.join(self, connection, channel)
  104. return
  105. do_everything_to.join(self, connection, channel, key)
  106. elif command.split()[0] == "part":
  107. if cmdtype == "help": #Display help text.
  108. if len(command.split()) is not 1:
  109. return
  110. connection.privmsg(replyto, "Make " + connection.get_nickname() + " part a channel. Reason optional.")
  111. connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
  112. elif cmdtype == "cmd":
  113. homeadmin = False
  114. if userstatus.atleast_oper(self, event.source.nick, self.homechannel): # Is at least operator in home channel.
  115. homeadmin = True
  116. targetadmin = False
  117. if userstatus.atleast_oper(self, event.source.nick, event.target):
  118. targetadmin = True
  119. if len(command.split()) == 1: # No arguments.
  120. if event.target in self.channels: # It's a channel message.
  121. if not homeadmin and not targetadmin: # Insufficient rights:
  122. connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + event.target + reset + ".")
  123. return
  124. if event.target == self.homechannel:
  125. connection.action("shall not abandon it's home channel!")
  126. return
  127. connection.part(event.target, event.source.nick)
  128. else: # It's a PM.
  129. connection.privmsg(replyto, "Specify a channel to part. For help type " + blue + self.helpchar + "part" + reset + ".")
  130. elif len(command.split()) > 1: # Arguments
  131. if command.split()[1] not in self.channels: # First argument is not a channel the bot inhabits.
  132. connection.action(replyto, "does not inhabit " + red + command.split()[1] + reset + ". For help type " + blue + self.helpchar + "part" + reset + ".")
  133. return
  134. if not homeadmin and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]): # Insufficient rights.
  135. connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + command.split()[1] + reset + ".")
  136. return
  137. if command.split()[1] == self.homechannel:
  138. connection.action("shall not abandon it's home channel!")
  139. return
  140. try:
  141. connection.part(command.split()[1], command.split(maxsplit=2)[2])
  142. except:
  143. connection.part(command.split()[1], event.source.nick)
  144. elif command.split()[0] == "msg" or command.split(maxsplit=1)[0] == "act":
  145. if cmdtype == "help": #Display help text.
  146. if len(command.split()) is not 1:
  147. return
  148. if command.split(maxsplit=1)[0] == "act":
  149. message = "Let " + connection.get_nickname() + " send an action to a channel."
  150. arguments = "action-message"
  151. else:
  152. message = "Let " + connection.get_nickname() + "send a message to a channel."
  153. arguments = "name message"
  154. connection.privmsg(replyto, message)
  155. connection.privmsg(replyto, grey + "Usage: " + blue + "!" + command.split(maxsplit=1)[0] + reset + italic + " target " + arguments)
  156. elif cmdtype == "cmd":
  157. # Parse user input.
  158. try:
  159. destination = trigger.split()[1]
  160. except IndexError: # User did not specify a destination.
  161. connection.privmsg(replyto, "Destination not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
  162. return
  163. try:
  164. message = trigger.split(maxsplit=2)[2]
  165. except IndexError: # User did not specify a message.
  166. connection.privmsg(replyto, "Message not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
  167. return
  168. # Send the message if user has owner status in the home channel.
  169. if self.channels[self.homechannel].is_owner(event.source.nick):
  170. if destination == connection.get_nickname():
  171. connection.privmsg(replyto, "To prevent dying in a loop I shall not message myself.")
  172. return
  173. if command.split(maxsplit=1)[0] == "act":
  174. connection.action(destination, message)
  175. else:
  176. connection.privmsg(destination, message)
  177. # Reply error when bot does not inhabit destination channel.
  178. elif destination not in self.channels:
  179. connection.action(replyto, "does not inhabit " + red + destination + reset + ".")
  180. # Send message if user has at least operator status the home channel or the channel the message is intended for.
  181. elif userstatus.atleast_oper(self, event.source.nick, self.homechannel) or userstatus.atleast_oper(self, event.source.nick, destination):
  182. if command.split(maxsplit=1)[0] == "act":
  183. connection.action(destination, message)
  184. else:
  185. connection.privmsg(destination, message)
  186. # Reply error if user is not operator of destination channel.
  187. else:
  188. connection.privmsg(replyto, "Denied, you need to be an operator of " + red + destination + reset +".")
  189. elif command.split()[0] == "channelfunctions":
  190. if cmdtype == "help": #Display help text. # Help code block first, as it is impossible to predict for what channel a later command is going to be issued. Rights filtering after help test.
  191. if len(command.split()) is not 1:
  192. return
  193. connection.privmsg(replyto, "Display or toggle the status channel functions. Channel, function and value optional. Get a description of a functio via the help argument.")
  194. connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions " + red + italic + "channel " + reset + italic + "function value")
  195. connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions describe " + reset + italic + "function")
  196. elif cmdtype == "cmd":
  197. if len(command.split()) == 1: # No arguments.
  198. if event.target == connection.get_nickname(): # Command issued via PM.
  199. connection.privmsg(replyto, "Nothing to display, Specify a channel.")
  200. else: # Command issued as channel message.
  201. message = AH.get_channelfunctions(self, event.target)
  202. connection.privmsg(replyto, message)
  203. elif len(command.split()) == 2: # One argument.
  204. if command.split()[1] in self.channels: # Info requested on specific channel.
  205. message = AH.get_channelfunctions(self, command.split()[1])
  206. connection.privmsg(replyto, message)
  207. else: # First argument is not a channel the bot inhabits.
  208. if not command.split()[1].lower() == "help": # Not a help request.
  209. connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  210. else: # Help request.
  211. connection.privmsg(replyto, "Specify a channel function to get a description of. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  212. elif len(command.split()) == 3: # Two arguments.
  213. channel = event.target
  214. if event.target == connection.get_nickname(): # Command issued via PM.
  215. connection.privmsg(replyto, "One or three arguments required. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  216. else: # Command issued via channel.
  217. if not AH.is_channelfunction(command.split()[1]): # First argument is not a channelfunction.
  218. if not command.split()[1].lower() == "describe": # Not a help request.
  219. connection.privmsg(replyto, command.split()[1] + " is not a channel function. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  220. return
  221. if not AH.is_channelfunction(command.split()[2]): # Second argument not a channel function.
  222. connection.privmsg(replyto, command.split()[2] + " is not a channel function.")
  223. return
  224. connection.privmsg(replyto, AH.describe_channelfunction(command.split()[2]))
  225. return
  226. # Second argument unsupported.
  227. if not command.split()[2].lower() in ["on", "off"]:
  228. if command.split()[1].lower() == "aggressiveness":
  229. if not AH.is_aggressiveness(command.split()[2].lower()): # Is not an aggressiveness setting.
  230. connection.privmsg(replyto, command.split()[2] + " is not an aggressiveness setting. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  231. return
  232. else: # Channel function is not aggresiveness.
  233. connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  234. return
  235. if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, event.target): # Does not have operator status or higher in target or home channel.
  236. connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + event.target + reset + " or " + red + self.homechannel + reset + ".")
  237. return
  238. if command.split()[1].lower() == "autojoin" and event.target == self.homechannel: # Chaning autojoin of homechannel.
  239. connection.action(replyto, "will always join it's homechannel " + red + self.homechannel + reset + ", regardless of the autojoin function.")
  240. #self.db.run("UPDATE channels SET " + command.split()[1].lower() + "='" + command.split()[2].lower() + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
  241. self.db.run("UPDATE channels SET " + command.split()[1].lower() + "=%s WHERE name='" + event.target + "' AND network='" + self.network + "'", (command.split()[2].lower(), ))
  242. elif len(command.split()) == 4: # Three arguments.
  243. if not command.split()[1] in self.channels: # Bot does not inhabit channel to be altered.
  244. connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  245. return
  246. if not AH.is_channelfunction(command.split()[2]): # Function does not exist.
  247. connection.privmsg(replyto, command.split()[2] + " is not a valid channel function. For a list help type: " + blue + self.cmdchar + "channelfunctions" + red + italic + "channel")
  248. return
  249. if not command.split()[3].lower() in ["on", "off"] and not command.split()[2].lower() == "aggressiveness": # Third argument unsupported.
  250. connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
  251. return
  252. if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]): # Does not have operator status or higher in target or home channel.
  253. connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + event.target + reset + " or " + red + self.homechannel + reset + ".")
  254. return
  255. if command.split()[2].lower() == "autojoin" and command.split()[1] == self.homechannel: # Chaning autojoin of homechannel.
  256. connection.action(replyto, "will always join it's homechannel " + red + self.homechannel + reset + ", regardless of the autojoin function.")
  257. try:
  258. self.db.run("UPDATE channels SET " + command.split()[2].lower() + "='" + command.split()[3].lower() + "' WHERE LOWER(name)=LOWER('" + command.split()[1] + "') AND network='" + self.network + "'")
  259. except:
  260. connection.privmsg(replyto, "Error, database record not updated.")
  261. return
  262. else: # Too many arguments.
  263. connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")