admin.py 19 KB

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