The KISS Twitch bot
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

3 年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # Twason - The KISS Twitch bot
  2. # Copyright (C) 2021 Jérôme Deuchnord
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as
  6. # published by the Free Software Foundation, either version 3 of the
  7. # License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. import json
  17. from os import environ
  18. from enum import Enum
  19. from typing import Union
  20. from .moderator import Moderator, CapsLockModerator, ModerationDecision
  21. class Command:
  22. name: str
  23. message: str
  24. aliases: [str]
  25. disabled: bool
  26. def __init__(self, name: str, message: str, aliases: [str] = None, disabled: bool = False):
  27. self.name = name
  28. self.message = message
  29. self.aliases = aliases if aliases is not None else []
  30. self.disabled = disabled
  31. @classmethod
  32. def from_dict(cls, params: dict):
  33. return Command(
  34. params.get('name'),
  35. params['message'],
  36. params.get('aliases', []),
  37. params.get('disabled', False)
  38. )
  39. class TimerStrategy(Enum):
  40. ROUND_ROBIN = "round-robin"
  41. SHUFFLE = "shuffle"
  42. class Timer:
  43. time_between: int
  44. msgs_between: int
  45. strategy: TimerStrategy
  46. pool: [Command]
  47. def __init__(
  48. self,
  49. time_between: int = 10,
  50. msgs_between: int = 10,
  51. strategy: TimerStrategy = TimerStrategy.ROUND_ROBIN,
  52. pool: [Command] = None
  53. ):
  54. self.time_between = time_between
  55. self.msgs_between = msgs_between
  56. self.strategy = strategy
  57. self.pool = pool if pool else []
  58. @classmethod
  59. def from_dict(cls, param: dict):
  60. pool = []
  61. for c in param.get('pool', []):
  62. command = Command.from_dict(c)
  63. if not command.disabled:
  64. pool.append(command)
  65. return Timer(
  66. time_between=param.get('between', {}).get('time', 10),
  67. msgs_between=param.get('between', {}).get('messages', 10),
  68. strategy=TimerStrategy(param.get('strategy', 'round-robin')),
  69. pool=pool
  70. )
  71. class Config:
  72. channel: str
  73. nickname: str
  74. token: str
  75. command_prefix: str
  76. commands: [Command]
  77. timer: Timer
  78. moderators: [Moderator]
  79. def __init__(
  80. self,
  81. channel: str,
  82. nickname: str,
  83. token: str,
  84. command_prefix: str,
  85. commands: [Command],
  86. timer: Timer,
  87. moderators: [Moderator]
  88. ):
  89. self.nickname = nickname
  90. self.channel = channel
  91. self.token = token
  92. self.command_prefix = command_prefix
  93. self.commands = commands
  94. self.timer = timer
  95. self.moderators = moderators
  96. @classmethod
  97. def from_dict(cls, params: dict, token: str):
  98. timer = Timer.from_dict(params.get('timer', {}))
  99. commands_prefix = params.get('command_prefix', '!')
  100. commands = []
  101. help_command = Command("help", "Voici les commandes disponibles : ")
  102. for command in params.get('commands', []):
  103. command = Command.from_dict(command)
  104. if command.disabled:
  105. continue
  106. commands.append(command)
  107. for command in timer.pool:
  108. if command.name is None:
  109. continue
  110. commands.append(command)
  111. moderators = []
  112. for moderator in params.get('moderator', []):
  113. decision_str = params['moderator']['caps-lock'].get("decision", "delete")
  114. if decision_str == "delete":
  115. decision = ModerationDecision.DELETE_MSG
  116. elif decision_str == "timeout":
  117. decision = ModerationDecision.TIMEOUT_USER
  118. else:
  119. print("WARNING: %s moderator's decision is invalid, it has been deactivated!")
  120. decision = ModerationDecision.ABSTAIN
  121. if moderator == 'caps-lock':
  122. moderators.append(CapsLockModerator(
  123. params['moderator']['caps-lock'].get("message", "{author}, stop the caps lock!"),
  124. params['moderator']['caps-lock'].get("min-size", 5),
  125. params['moderator']['caps-lock'].get("threshold", 50),
  126. decision,
  127. params['moderator']['caps-lock'].get("duration", None)
  128. ))
  129. # Generate help command
  130. if params.get('help', True):
  131. for command in commands:
  132. help_command.message = "%s %s%s" % (help_command.message, commands_prefix, command.name)
  133. commands.append(help_command)
  134. return Config(
  135. params.get('channel'),
  136. params.get('nickname'),
  137. token,
  138. commands_prefix,
  139. commands,
  140. timer,
  141. moderators
  142. )
  143. def find_command(self, command: str) -> Union[None, Command]:
  144. if not command.startswith(self.command_prefix):
  145. return None
  146. command = command[1:]
  147. for c in self.commands:
  148. if c.name == command or command in c.aliases:
  149. return c
  150. return None
  151. def get_config(file_path: str):
  152. with open(file_path, 'r') as config_file:
  153. token = environ['TWITCH_TOKEN']
  154. return Config.from_dict(json.loads(config_file.read()), token)