Skip to content

Commit c3afbfe

Browse files
authored
Merge pull request #80 from iayushanand/master
added tickets, lyrics cmds and made afk lvl 30+
2 parents 4cc8d23 + 96e9fea commit c3afbfe

File tree

10 files changed

+493
-43
lines changed

10 files changed

+493
-43
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
TOKEN =
22
MODMAIL_WEBHOOK_URL =
3-
GEMINI_API_KEY =
3+
GEMINI_API_KEY =
4+
GITHUB_TOKEN =

cogs/misc.py

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
import random
77
import re
88
from typing import TYPE_CHECKING, Optional
9-
9+
from discord import app_commands
1010
import button_paginator as pg
1111
import contextlib
1212
import discord
1313
from discord.ext import commands
14-
from ext.helpers import Spotify, grouper, ordinal_suffix_of, gemini_split_string
14+
from ext.helpers import Spotify, grouper, ordinal_suffix_of, gemini_split_string, get_lyrics, find_surrounding_lyrics, filter_banned_words
1515
from ext.http import Http
1616
from ext.ui.view import Piston
17-
17+
import time
1818
import google.generativeai as genai
1919
import button_paginator as pg
2020
from googletrans import Translator
@@ -23,6 +23,44 @@
2323
from ext.models import CodingBot
2424

2525

26+
27+
# I don't like the way its implemented but can't find a better way to do it so if someone can make this command better, that'll be great!
28+
29+
@app_commands.context_menu(name = "translate")
30+
async def translate(interaction: discord.Interaction, message: discord.Message):
31+
await interaction.response.defer(ephemeral = True)
32+
trans = message.content
33+
try:
34+
translated = Translator(service_urls=[
35+
'translate.google.com',
36+
'translate.google.co.kr'
37+
]).translate(trans)
38+
except Exception as e:
39+
raise e
40+
41+
embed = discord.Embed()
42+
43+
_from = translated.src.upper()
44+
_to = translated.dest.upper()
45+
46+
embed.add_field(
47+
name = f"Original ({_from})",
48+
value = trans,
49+
inline = False
50+
)
51+
embed.add_field(
52+
name = f"Translated ({_to})",
53+
value = translated.text,
54+
inline = False
55+
)
56+
57+
await interaction.followup.send(
58+
embed = embed,
59+
ephemeral = True
60+
)
61+
62+
63+
2664
class Miscellaneous(commands.Cog, command_attrs=dict(hidden=False)):
2765
hidden = False
2866

@@ -40,6 +78,9 @@ async def cog_check(self, ctx: commands.Context[CodingBot]) -> bool:
4078
return True
4179
await ctx.send("Please use commands in the server instead of dms")
4280
return False
81+
82+
async def cog_load(self):
83+
self.bot.tree.add_command(translate)
4384

4485
@commands.hybrid_command(
4586
name="retry",
@@ -66,6 +107,7 @@ async def retry(self, ctx: commands.Context[CodingBot]):
66107
name="afk", aliases=["afk-set", "set-afk"], help="Sets your afk"
67108
)
68109
@commands.cooldown(1, 10, commands.BucketType.member)
110+
@commands.has_role(734283436637814844) # lvl 30+
69111
async def afk(
70112
self, ctx: commands.Context[CodingBot], *, reason: Optional[str] = None
71113
):
@@ -496,46 +538,42 @@ async def on_timeout():
496538

497539
await paginator.start()
498540

499-
@commands.command(name = "translate")
500-
async def translate(self, ctx: commands.Context, *, text: str = None):
501-
if not ctx.message.reference and not text:
502-
return await ctx.reply("Please reply a message or provide a text to translate!")
503-
504-
if text:
505-
trans = text
506-
message = ctx.message
507-
else:
508-
message = await ctx.channel.fetch_message(ctx.message.reference.message_id)
509-
trans = message.content
510541

511-
try:
512-
translated = Translator(service_urls=[
513-
'translate.google.com',
514-
'translate.google.co.kr'
515-
]).translate(trans)
516-
except Exception as e:
517-
raise e
542+
@commands.hybrid_command(name = "lyric", aliases = ["lyrics"])
543+
async def lyric(self, ctx: commands.Context, member: discord.Member = None):
544+
member = member or ctx.author
518545

519-
embed = discord.Embed()
546+
spotify_activity = None
547+
for activity in member.activities:
548+
if isinstance(activity, discord.Spotify):
549+
spotify_activity = activity
550+
break
551+
552+
if not spotify_activity:
553+
await ctx.send(f"{member.display_name} is not listening to Spotify.")
554+
return
555+
556+
else:
557+
duration = time.time()-spotify_activity.start.timestamp()
558+
song_title = spotify_activity.title
559+
song_artist = spotify_activity.artist
560+
lyr = get_lyrics(song_title, song_artist)
561+
if not lyr:
562+
return await ctx.send(f"Lyrics not found for song - {song_title} - {song_artist}")
563+
if lyr[1] == 1:
564+
lyr = "\n".join(find_surrounding_lyrics(lyr[0], int(duration)))
565+
else:
566+
lyr = "\n".join(lyr.splitlines()[0:5])
567+
568+
lyr = filter_banned_words(lyr)
569+
embed = discord.Embed(description = lyr, title = f"{song_title} - {song_artist}", color = spotify_activity.color)
570+
embed.set_author(name = ctx.author, icon_url = ctx.author.avatar.url)
571+
embed.set_thumbnail(url = spotify_activity.album_cover_url)
572+
573+
await ctx.send(embed = embed)
574+
520575

521-
_from = translated.src.upper()
522-
_to = translated.dest.upper()
523-
524-
embed.add_field(
525-
name = f"Original ({_from})",
526-
value = trans,
527-
inline = False
528-
)
529-
embed.add_field(
530-
name = f"Translated ({_to})",
531-
value = translated.text,
532-
inline = False
533-
)
534576

535-
await message.reply(
536-
embed = embed,
537-
allowed_mentions=discord.AllowedMentions.none()
538-
)
539577

540578

541579
async def setup(bot: CodingBot):

cogs/ticket.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import discord
2+
from discord.ext import commands
3+
4+
from ext.ui.view import CreateButton, CloseButton, TrashButton
5+
6+
class TicketCog(commands.Cog):
7+
def __init__(self, bot: commands.Bot):
8+
self.bot = bot
9+
10+
@commands.Cog.listener(name = "on_ready")
11+
async def before_ready(self):
12+
self.bot.add_view(CreateButton())
13+
self.bot.add_view(CloseButton())
14+
self.bot.add_view(TrashButton())
15+
await self.bot.change_presence(activity=discord.Activity(type = discord.ActivityType.listening, name = "Doghouse Game's Heart 😳"))
16+
print(f"Logged in as: {self.bot.user.name}")
17+
18+
@commands.command(name="ticket")
19+
@commands.has_permissions(administrator=True)
20+
async def ticket(self, ctx):
21+
await ctx.send(
22+
embed = discord.Embed(
23+
description="🎫 **Click on the button below to create a ticket**\nIf you need any help regarding punishments, roles, or you just have a general question, feel free to create a ticket and a staff member will get to you shortly!\nOpening a ticket without a valid reason will get you warned/blacklisted.\n\n__**Do not open support tickets for Coding Help. Doing so will get you warned.**__",
24+
color = 0x8b6ffc
25+
),
26+
view = CreateButton()
27+
)
28+
29+
async def setup(bot):
30+
await bot.add_cog(TicketCog(bot))

ext/consts.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ def release_format(self):
132132
UNIQUE(user_id, guild_id)
133133
);"""
134134

135+
136+
TICKETS_CONFIG_SCHEMA = """
137+
CREATE TABLE IF NOT EXISTS tickets (
138+
message_id BIGINT PRIMARY KEY,
139+
ticket_id BIGINT,
140+
opened_by BIGINT,
141+
closed_by BIGINT,
142+
opened_at BIGINT,
143+
closed_at BIGINT,
144+
reason TEXT
145+
)
146+
"""
147+
148+
135149
HELP_COMMAND = """
136150
Help command for Coding Bot
137151
@@ -157,5 +171,12 @@ def release_format(self):
157171
MODMAIL_CLOSED = 1144842686579359837 # conch: 1144839107353256017
158172
STAFF_UPDATE_CHANNEL_ID = 1124612885365133412
159173

174+
175+
TICKET_REPO = "WhoIsConch/tcrtickets"
176+
TICKET_HANDLER_ROLE_ID = 788799215417032705
177+
OPEN_TICKET_CATEGORY = 788797663377883147
178+
CLOSED_TICKET_CATEGORY = 1287954797286391860
179+
TICKET_LOG_CHANNEL = 829936676021075999
180+
160181
# mods pls fill this up
161182
TICKET_CATEGORY_ID = 0 # <--- this still hasn't been resolved but I don't think we need it ~ ayu

ext/helpers.py

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@
1111
import traceback
1212
from io import BytesIO
1313
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
14-
14+
from lrclib import LrcLibAPI
1515
import aiohttp
1616
import discord
1717
import humanize
1818
from bs4 import BeautifulSoup
1919
from discord.ext import tasks
2020
from PIL import Image, ImageDraw, ImageFilter, ImageFont
2121
from cbvx import iml
22+
from ext.consts import TCR_STAFF_ROLE_ID, TICKET_REPO
2223

23-
from ext.consts import TCR_STAFF_ROLE_ID
24+
import chat_exporter
25+
from github import Github
26+
import os
2427

2528
if TYPE_CHECKING:
2629
from ext.models import CodingBot
@@ -781,3 +784,93 @@ def invert_string(text):
781784

782785
def gemini_split_string(string, chunk_size=1000):
783786
return [string[i:i+chunk_size] for i in range(0, len(string), chunk_size)]
787+
788+
789+
790+
791+
# GET TRANSCRIPT
792+
async def get_transcript(member: discord.Member, channel: discord.TextChannel):
793+
export = await chat_exporter.export(channel=channel)
794+
file_name=f"{member.id}.html"
795+
with open(f"storage/tickets/{file_name}", "w", encoding="utf-8") as f:
796+
f.write(export)
797+
798+
# UPLOAD TO GITHUB
799+
def upload(file_path: str, member_name: str, file_name: str):
800+
github = Github(os.getenv("GITHUB_TOKEN"))
801+
repo = github.get_repo(TICKET_REPO)
802+
repo.create_file(
803+
path=f"templates/tickets/{file_name}.html",
804+
message="Ticket Log for {0}".format(member_name),
805+
branch="main",
806+
content=open(f"{file_path}","r",encoding="utf-8").read()
807+
)
808+
os.remove(file_path)
809+
810+
return file_name
811+
812+
813+
814+
def get_lyrics(name: str, artist: str) -> str:
815+
user_agent = (
816+
"Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0"
817+
)
818+
819+
# Prepare the API
820+
api = LrcLibAPI(user_agent=user_agent)
821+
id = api.search_lyrics(track_name=name, artist_name=artist)
822+
823+
# Check if lyrics are available
824+
if len(id) != 0:
825+
lyrics = api.get_lyrics_by_id(id[0].id)
826+
s_lyrics = lyrics.synced_lyrics, 1
827+
if not s_lyrics:
828+
s_lyrics = lyrics.plain_lyrics, 0
829+
return s_lyrics
830+
else:
831+
return None
832+
833+
834+
def parse_timestamp_to_seconds(timestamp):
835+
minutes, seconds = map(float, timestamp.split(':'))
836+
return minutes * 60 + seconds
837+
838+
def find_surrounding_lyrics(lyrics, target_seconds):
839+
pattern = r'\[(\d{2}:\d{2}\.\d{2})\] (.+)'
840+
matches = re.findall(pattern, lyrics)
841+
842+
parsed_lyrics = []
843+
844+
for match in matches:
845+
timestamp, lyric = match
846+
time_in_seconds = parse_timestamp_to_seconds(timestamp)
847+
parsed_lyrics.append((time_in_seconds, lyric))
848+
849+
closest_index = min(range(len(parsed_lyrics)), key=lambda i: abs(parsed_lyrics[i][0] - target_seconds))
850+
851+
start_index = max(closest_index - 2, 0)
852+
end_index = min(closest_index + 3, len(parsed_lyrics))
853+
854+
surrounding_lyrics = [parsed_lyrics[i][1] for i in range(start_index, end_index)]
855+
856+
return surrounding_lyrics
857+
858+
859+
860+
def filter_banned_words(text: str):
861+
with open("storage/banned_word.txt") as f:
862+
words = f.read().split(", ")
863+
864+
new_text = text
865+
for word in words:
866+
pattern = re.compile(re.escape(word), re.IGNORECASE)
867+
868+
def censor(match):
869+
matched_word = match.group()
870+
word_length = len(matched_word) // 2
871+
stars = "\*" * word_length
872+
return f"{stars}{matched_word[word_length:]}"
873+
874+
new_text = pattern.sub(censor, new_text)
875+
876+
return new_text

ext/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
THANK_DATA_CONFIG_SCHEMA,
3737
THANK_INFO_CONFIG_SCHEMA,
3838
WARNINGS_CONFIG_SCHEMA,
39+
TICKETS_CONFIG_SCHEMA,
3940
VERSION,
4041
Version,
4142
)
@@ -133,6 +134,7 @@ async def __aenter__(self) -> "Database":
133134
self.conn["afk"] = await aiosqlite.connect("./database/afk.db")
134135
self.conn["thanks"] = await aiosqlite.connect("./database/thanks.db")
135136
self.conn["metrics"] = await aiosqlite.connect("./database/metrics.db")
137+
self.conn["tickets"] = await aiosqlite.connect("./database/tickets.db")
136138
await self.init_dbs()
137139
self.bot.logger.info("Finished creating all connections")
138140
return self
@@ -164,6 +166,9 @@ async def init_dbs(self):
164166

165167
async with self.cursor("metrics") as cursor:
166168
await cursor.execute(MESSAGE_METRIC_SCHEMA)
169+
170+
async with self.cursor("tickets") as cursor:
171+
await cursor.execute(TICKETS_CONFIG_SCHEMA)
167172

168173
await self.commit()
169174

0 commit comments

Comments
 (0)