A multipurpose discord bot.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

starboard.py 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. # kirigiri - A discord bot.
  2. # Copyright (C) 2019 - Valentijn "noirscape" V.
  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 published
  6. # by the Free Software Foundation at version 3 of the License.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU Affero General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU Affero General Public License
  14. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. #
  16. # In addition, the additional clauses 7b and 7c are in effect for this program.
  17. #
  18. # b) Requiring preservation of specified reasonable legal notices or
  19. # author attributions in that material or in the Appropriate Legal
  20. # Notices displayed by works containing it; or
  21. #
  22. # c) Prohibiting misrepresentation of the origin of that material, or
  23. # requiring that modified versions of such material be marked in
  24. # reasonable ways as different from the original version
  25. # Starboard.
  26. # Idea:
  27. # - User adds reaction (star) to message
  28. # - x stars (user settable?) or higher mean a post in starboard channel.
  29. # - Bot keeps track of added stars and edits the starboard on each added and removed reaction.
  30. # - Different emojis the more stars something has? (star, glowing star, dizzy, sparkles)
  31. # - Need to have error handling in case message fails to be edited.
  32. import discord
  33. from discord.ext import commands
  34. from db import StarboardChannel, StarredMessage, VoteConfig
  35. import textwrap
  36. from utils.intermediaryreaction import IntermediaryReaction
  37. class Starboard(commands.Cog):
  38. stars = {
  39. 1: '✴',
  40. 2: '⭐',
  41. 5: '🌟',
  42. 10: '💫',
  43. 15: '✨'
  44. }
  45. def __init__(self, bot):
  46. self.bot = bot
  47. async def get_star(self, idx):
  48. star = None
  49. while not star:
  50. if idx in self.stars:
  51. star = self.stars[idx]
  52. idx -= 1
  53. return star
  54. @commands.command()
  55. @commands.has_permissions(administrator=True)
  56. async def set_starboard_channel(self, ctx, channel: discord.TextChannel=None):
  57. """Set the starboard channel. Leave empty to unset."""
  58. if not channel:
  59. session = self.bot.db.dbsession()
  60. starboard_channel = session.query(StarboardChannel).filter_by(guild_id=ctx.guild.id).one()
  61. if starboard_channel:
  62. session.delete(starboard_channel)
  63. session.commit()
  64. session.close()
  65. return await ctx.send("Starboard channel unset.")
  66. else:
  67. session.close()
  68. return await ctx.send("No starboard channel set!")
  69. if channel not in ctx.guild.channels:
  70. return await ctx.send("This channel is not in this guild!")
  71. session = self.bot.db.dbsession()
  72. new_starboard_channel = StarboardChannel(channel_id=channel.id, guild_id=ctx.guild.id)
  73. try:
  74. vote_config = session.query(VoteConfig).filter_by(guild_id=ctx.guild.id).one()
  75. except:
  76. vote_config = None
  77. if not vote_config:
  78. new_votes = VoteConfig(guild_id=ctx.guild.id, minimum_votes=3)
  79. session.merge(new_votes)
  80. session.merge(new_starboard_channel)
  81. session.commit()
  82. session.close()
  83. return await ctx.send(f"Starboard channel has been set to {channel}.")
  84. @commands.command()
  85. @commands.has_permissions(administrator=True)
  86. async def set_star_count(self, ctx, vote_count: int):
  87. """Set minimum amount of stars needed to make the bot post a message."""
  88. if vote_count <= 0:
  89. return await ctx.send("Invalid vote count!")
  90. session = self.bot.db.dbsession()
  91. new_star_count = VoteConfig(guild_id=ctx.guild.id, minimum_votes=vote_count)
  92. session.merge(new_star_count)
  93. session.commit()
  94. session.close()
  95. await ctx.send(f"Minimum stars set to {vote_count}.")
  96. async def generate_starboard_message(self, star_count, original_message):
  97. """Generate a message to be put on the starboard.
  98. Returns a tuple:
  99. embed, content
  100. Embed is the embed to send.
  101. Content is a string to send along.
  102. """
  103. embed = discord.Embed()
  104. if original_message.content:
  105. embed.description = textwrap.shorten(original_message.content, 2000)
  106. if original_message.attachments:
  107. embed.set_image(url=original_message.attachments[0].url)
  108. embed.set_author(name=str(original_message.author), icon_url=original_message.author.avatar_url)
  109. embed.set_footer(text=str(original_message.created_at))
  110. star = await self.get_star(star_count)
  111. content = f"{star} {star_count} - Channel: {original_message.channel.mention} - ID: {original_message.id}"
  112. return embed, content
  113. @commands.Cog.listener()
  114. async def on_raw_reaction_add(self, raw_reaction):
  115. if str(raw_reaction.emoji) == '⭐':
  116. reaction = await IntermediaryReaction.create(raw_reaction, self.bot)
  117. session = self.bot.db.dbsession()
  118. try:
  119. channel_configured = session.query(StarboardChannel).filter_by(guild_id=reaction.message.guild.id).one()
  120. except:
  121. channel_configured = None
  122. try:
  123. minimum_votes = session.query(VoteConfig).filter_by(guild_id=reaction.message.guild.id).one()
  124. except:
  125. minimum_votes = None
  126. if channel_configured and minimum_votes:
  127. try:
  128. message_db_object = session.query(StarredMessage).filter_by(message_id=reaction.message.id).one()
  129. except:
  130. message_db_object = None
  131. if message_db_object:
  132. message_db_object.reaction_count += 1
  133. embed, content = await self.generate_starboard_message(message_db_object.reaction_count, reaction.message)
  134. starboard_message = await self.bot.get_channel(channel_configured.channel_id).fetch_message(message_db_object.starboard_message_id)
  135. await starboard_message.edit(content=content, embed=embed)
  136. else:
  137. embed, content = await self.generate_starboard_message(1, reaction.message)
  138. starboard_message = await self.bot.get_channel(channel_configured.channel_id).send(embed=embed, content=content)
  139. message_db_object = StarredMessage(message_id=reaction.message.id, reaction_count=1, starboard_message_id=starboard_message.id)
  140. session.merge(message_db_object)
  141. session.commit()
  142. session.close()
  143. @commands.Cog.listener()
  144. async def on_raw_reaction_remove(self, raw_reaction):
  145. if str(raw_reaction.emoji) == '⭐':
  146. reaction = await IntermediaryReaction.create(raw_reaction, self.bot)
  147. session = self.bot.db.dbsession()
  148. try:
  149. channel_configured = session.query(StarboardChannel).filter_by(guild_id=reaction.message.guild.id).one()
  150. except:
  151. channel_configured = None
  152. try:
  153. minimum_votes = session.query(VoteConfig).filter_by(guild_id=reaction.message.guild.id).one()
  154. minimum_votes = minimum_votes.minimum_votes
  155. except:
  156. minimum_votes = 0
  157. try:
  158. message_db_object = session.query(StarredMessage).filter_by(message_id=reaction.message.id).one()
  159. except:
  160. message_db_object = None
  161. if not message_db_object:
  162. session.commit()
  163. session.close()
  164. return
  165. message_db_object.reaction_count -= 1
  166. starboard_message = await self.bot.get_channel(channel_configured.channel_id).fetch_message(message_db_object.starboard_message_id)
  167. if message_db_object.reaction_count < minimum_votes:
  168. if starboard_message:
  169. await starboard_message.delete()
  170. session.delete(message_db_object)
  171. else:
  172. embed, content = await self.generate_starboard_message(message_db_object.reaction_count, reaction.message)
  173. await starboard_message.edit(content=content, embed=embed)
  174. session.merge(message_db_object)
  175. session.commit()
  176. session.close()
  177. def setup(bot):
  178. bot.add_cog(Starboard(bot))