├── runtime.txt
├── requirements.txt
├── .github
├── dependabot.yml
└── FUNDING.yml
├── LICENSE
├── PRIVACY_POLICY.md
├── .gitignore
├── README.md
├── cogs
├── devtools.py
└── neetcode.py
├── TERMS_OF_SERVICE.md
└── main.py
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.11.1
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiohttp==3.9.1
2 | discord.py==2.3.2
3 | python-dotenv==1.0.0
4 | utils==1.0.1
5 | GitPython==3.1.40
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "pip" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "monthly"
12 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [abe-101] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 NYC Code & Coffee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PRIVACY_POLICY.md:
--------------------------------------------------------------------------------
1 | # Privacy Policy
2 |
3 | Last updated: February 11, 2025
4 |
5 | ## Introduction
6 | This Privacy Policy describes how the Leetcode Bot ("we", "us", or "our") collects, uses, and handles your information when you use our Discord bot service.
7 |
8 | ## Information We Collect
9 | ### Automatically Collected Information
10 | - Discord User ID
11 | - Discord Server ID
12 | - Command usage data
13 | - Leetcode problem numbers and programming languages requested
14 |
15 | ### We DO NOT Collect
16 | - Personal messages
17 | - Direct message content
18 | - User profile information
19 | - Any information outside of bot interactions
20 |
21 | ## How We Use Information
22 | We use the collected information for:
23 | - Providing leetcode solutions
24 | - Tracking command usage for service improvements
25 | - Generating anonymous usage statistics
26 | - Debugging and error monitoring
27 |
28 | ## Data Storage
29 | - All data is stored temporarily in memory and log files
30 | - Command usage statistics are reset daily
31 | - We do not maintain any permanent user databases
32 | - Log files are automatically rotated and old logs are deleted
33 |
34 | ## Data Sharing
35 | We do not share any user data with third parties. The only data visible to other users are:
36 | - Public command interactions in Discord channels
37 | - Solutions to leetcode problems (which are publicly available)
38 |
39 | ## Security
40 | We implement appropriate security measures to protect the limited data we collect:
41 | - Secure bot token storage
42 | - Regular security updates
43 | - Limited access to log files
44 |
45 | ## User Rights
46 | You have the right to:
47 | - Use the bot privately through ephemeral messages
48 | - Request deletion of your data (though we store minimal data)
49 | - Opt out of using the bot at any time
50 |
51 | ## Changes to This Policy
52 | We may update this Privacy Policy from time to time. We will notify users of any material changes by posting the new Privacy Policy in our GitHub repository.
53 |
54 | ## Contact
55 | If you have any questions about this Privacy Policy, please contact us:
56 | - Through our GitHub repository: https://github.com/abe-101/abe-101-leetcode-bot
57 | - Discord: Join our support server at https://discord.gg/2vqUzSpt6N
58 |
59 | ## Open Source
60 | This bot is open source, and you can verify our data practices by reviewing our code on GitHub.
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 | leetcode
131 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Leetcode bot
2 | [](https://gist.github.com/cheerfulstoic/d107229326a01ff0f333a1d3476e068d)
3 | [](https://discord.gg/2vqUzSpt6N)
4 |
5 | This project is a Discord bot that can provide solutions to
6 | [leetcode](https://leetcode.com/) problems in
7 | up to 13 programming languages. The bot is designed to help programmers prepare
8 | for coding interviews by providing quick and easy access to solutions in multiple
9 | languages. To use the bot, simply send a message containing the LeetCode problem
10 | number and the desired programming language. The bot will respond with the solution.
11 | The bot is built using the [Discord.py](https://github.com/Rapptz/discord.py) framework.
12 | This project is open-source and contributions are welcome!
13 |
14 | The bot is Currently active on the NeetCode server, which has a community of over 30,000 developers, and also available for invite to other Discord servers.
15 |
16 | ## Servers
17 | Leetcode bot is currently active on over 25 servers including:
18 | - [Neetcode](https://discord.gg/ddjKRXPqtk)
19 | - [Code & Coffee](https://www.codeandcoffee.chat/)
20 | - [abe's server](https://discord.gg/Bv8XkBSEHP)
21 |
22 |
23 | ## Features
24 | - Provides solutions to LeetCode problems in multiple programming languages
25 | - Easy to use commands for requesting solutions
26 |
27 | ## Usage
28 | To request a solution to a LeetCode problem, use the following command in a Discord channel where the bot is active:
29 |
30 | `/leetcode `
31 |
32 | For example, to request a solution to LeetCode problem #1 in Python, you would use the following command:
33 |
34 | `/leetcode 1 python`
35 |
36 | ## Invite to your server
37 | You can invite the bot to your own Discord server using the following link:
38 |
39 | [Invite me](https://discord.com/oauth2/authorize?client_id=1052787378718253106&scope=bot+applications.commands&permissions=0)
40 |
41 | ## Technologies and tools
42 | - [Discord.py](https://github.com/Rapptz/discord.py)
43 | - Python
44 |
45 | ## See it in action
46 |
47 | [demo video.webm](https://user-images.githubusercontent.com/82916197/208010250-54f21d02-8774-4953-aa93-92a8bfd0f31d.webm)
48 |
--------------------------------------------------------------------------------
/cogs/devtools.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import typing
3 |
4 | import discord
5 | from discord import app_commands
6 | from discord.ext import commands
7 | from discord.ext.commands import Context
8 |
9 | _logger = logging.getLogger(__name__)
10 |
11 |
12 | class DevTools(commands.Cog):
13 | def __init__(self, bot: commands.Bot) -> None:
14 | self.bot = bot
15 |
16 | @commands.command()
17 | @commands.guild_only()
18 | @commands.is_owner()
19 | async def sync(
20 | self,
21 | ctx: commands.Context,
22 | guilds: commands.Greedy[discord.Object],
23 | spec: typing.Optional[typing.Literal["~", "*", "^"]] = None,
24 | ) -> None:
25 | """Syncs command tree.
26 | Parameters
27 | -----------
28 | guilds: list[int]
29 | The guilds to sync to
30 | spec: str
31 | The spec to sync.
32 | ~ -> Current Guild
33 | * -> Globals to current guild
34 | ^ -> Clear globals copied to current guild.
35 | """
36 | if not guilds:
37 | if spec == "~":
38 | synced = await ctx.bot.tree.sync(guild=ctx.guild)
39 | elif spec == "*":
40 | ctx.bot.tree.copy_global_to(guild=ctx.guild)
41 | synced = await ctx.bot.tree.sync(guild=ctx.guild)
42 | elif spec == "^":
43 | ctx.bot.tree.clear_commands(guild=ctx.guild)
44 | await ctx.bot.tree.sync(guild=ctx.guild)
45 | synced = []
46 | else:
47 | synced = await ctx.bot.tree.sync()
48 | await ctx.send(
49 | f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}"
50 | )
51 | return
52 | ret = 0
53 | for guild in guilds:
54 | try:
55 | await ctx.bot.tree.sync(guild=guild)
56 | except discord.HTTPException:
57 | pass
58 | else:
59 | ret += 1
60 | await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.")
61 |
62 | @commands.command(aliases=['invite'])
63 | async def join(self, ctx: Context):
64 | """Posts my invite to allow you to invite me"""
65 | perms = discord.Permissions.none()
66 | await ctx.send(f'<{discord.utils.oauth_url(self.bot.client_id, permissions=perms)}>')
67 |
68 |
69 |
70 | async def setup(bot):
71 | _logger.info("Loading DevTools cog")
72 | await bot.add_cog(DevTools(bot))
73 |
74 |
75 | async def teardown(_):
76 | _logger.info("Extension: Unloading DevTools")
77 |
--------------------------------------------------------------------------------
/TERMS_OF_SERVICE.md:
--------------------------------------------------------------------------------
1 | # Terms of Service
2 |
3 | Last updated: February 11, 2025
4 |
5 | ## Agreement to Terms
6 | By using the Leetcode Bot ("the Bot"), you agree to these Terms of Service. If you disagree with any part of these terms, you do not have permission to use the Bot.
7 |
8 | ## Description of Service
9 | The Leetcode Bot is a Discord bot that provides:
10 | - Solutions to Leetcode problems in multiple programming languages
11 | - Access to the Neetcode solutions repository
12 | - Programming language support through Discord commands
13 |
14 | ## Usage Requirements
15 | To use the Bot, you must:
16 | - Be a member of a Discord server where the Bot is installed
17 | - Follow Discord's Terms of Service and Community Guidelines
18 | - Use the Bot only for its intended purpose
19 | - Not attempt to abuse, exploit, or circumvent the Bot's limitations
20 |
21 | ## Acceptable Use
22 | You agree not to:
23 | - Use the Bot to violate any laws or regulations
24 | - Attempt to access, tamper with, or use non-public areas of the Bot
25 | - Probe, scan, or test the vulnerability of the Bot
26 | - Spam commands or intentionally overload the Bot
27 | - Share solutions in violation of Leetcode's terms of service
28 |
29 | ## Intellectual Property
30 | - The Bot's code is open source under the MIT License
31 | - Leetcode solutions are provided through the Neetcode repository
32 | - Users are responsible for adhering to Leetcode's terms when using solutions
33 |
34 | ## Disclaimer of Warranty
35 | The Bot is provided "AS IS" and "AS AVAILABLE" without warranties of any kind, either express or implied, including but not limited to:
36 | - Accuracy of solutions
37 | - Availability of the service
38 | - Reliability of the service
39 |
40 | ## Limitation of Liability
41 | We shall not be liable for any indirect, incidental, special, consequential, or punitive damages resulting from:
42 | - Use or inability to use the Bot
43 | - Any solutions provided by the Bot
44 | - Any changes to the service
45 |
46 | ## Service Modifications
47 | We reserve the right to:
48 | - Modify or discontinue the Bot temporarily or permanently
49 | - Change these Terms of Service at any time
50 | - Limit Bot usage or features as needed
51 |
52 | ## Third-Party Services
53 | The Bot interacts with third-party services including:
54 | - Discord
55 | - Leetcode
56 | - Neetcode GitHub repository
57 | Users are subject to the respective terms and policies of these services.
58 |
59 | ## Termination
60 | We reserve the right to:
61 | - Terminate or suspend access to the Bot without prior notice
62 | - Remove the Bot from any Discord server
63 | - Block users who violate these terms
64 |
65 | ## Contact Information
66 | For questions about these Terms:
67 | - Visit our GitHub repository: https://github.com/abe-101/abe-101-leetcode-bot
68 | - Join our Discord support server: https://discord.gg/2vqUzSpt6N
69 |
70 | ## Open Source
71 | This bot is open source under the MIT License. You can view, fork, and contribute to the code on GitHub.
72 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | from logging.handlers import RotatingFileHandler
4 |
5 | import discord
6 | from discord.ext import commands
7 | from dotenv import load_dotenv
8 |
9 |
10 | # Setup basic logging
11 |
12 | load_dotenv()
13 |
14 | DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
15 |
16 |
17 | class Bot(commands.Bot):
18 | def __init__(self):
19 | intents = discord.Intents.default()
20 | intents.message_content = True
21 | intents.members = True
22 | self.client_id = None
23 |
24 | super().__init__(
25 | command_prefix=commands.when_mentioned_or("?"),
26 | intents=intents,
27 | activity=discord.Game(name="💻"),
28 | )
29 |
30 | async def on_ready(self):
31 | self.logger.info(f"Logged in as {self.user} (ID: {self.user.id})")
32 | self.logger.info("------")
33 | self.client_id = self.user.id
34 |
35 | async def setup_hook(self) -> None:
36 | # Load cogs
37 | for file in os.listdir(f"./cogs"):
38 | if file.endswith(".py"):
39 | extension = file[:-3]
40 | try:
41 | await bot.load_extension(f"cogs.{extension}")
42 | self.logger.info(f"Loaded extension '{extension}'")
43 | except Exception as e:
44 | self.logger.exception(f"Failed to load extension {extension}")
45 |
46 |
47 | class LoggingFormatter(logging.Formatter):
48 | # Colors
49 | black = "\x1b[30m"
50 | red = "\x1b[31m"
51 | green = "\x1b[32m"
52 | yellow = "\x1b[33m"
53 | blue = "\x1b[34m"
54 | gray = "\x1b[38m"
55 | # Styles
56 | reset = "\x1b[0m"
57 | bold = "\x1b[1m"
58 |
59 | COLORS = {
60 | logging.DEBUG: gray + bold,
61 | logging.INFO: blue + bold,
62 | logging.WARNING: yellow + bold,
63 | logging.ERROR: red,
64 | logging.CRITICAL: red + bold,
65 | }
66 |
67 | def format(self, record):
68 | log_color = self.COLORS[record.levelno]
69 | format = "(black){asctime}(reset) (levelcolor){levelname:<8}(reset) (green){name}(reset) {message}"
70 | format = format.replace("(black)", self.black + self.bold)
71 | format = format.replace("(reset)", self.reset)
72 | format = format.replace("(levelcolor)", log_color)
73 | format = format.replace("(green)", self.green + self.bold)
74 | formatter = logging.Formatter(format, "%Y-%m-%d %H:%M:%S", style="{")
75 | return formatter.format(record)
76 |
77 |
78 | logger = logging.getLogger("discord_bot")
79 | logger.setLevel(logging.INFO)
80 |
81 | # Console handler
82 | console_handler = logging.StreamHandler()
83 | console_handler.setFormatter(LoggingFormatter())
84 | ## File handler
85 | file_handler = RotatingFileHandler(
86 | filename="discord.log",
87 | encoding="utf-8",
88 | mode="a",
89 | maxBytes=1024 * 1024,
90 | backupCount=5,
91 | )
92 | file_handler_formatter = logging.Formatter(
93 | "[{asctime}] [{levelname:<8}] {name}: {message}", "%Y-%m-%d %H:%M:%S", style="{"
94 | )
95 | file_handler.setFormatter(file_handler_formatter)
96 |
97 |
98 | # Add the handlers
99 | logger.addHandler(console_handler)
100 | logger.addHandler(file_handler)
101 |
102 | bot = Bot()
103 | bot.logger = logger
104 |
105 | bot.run(DISCORD_TOKEN)
106 |
107 |
--------------------------------------------------------------------------------
/cogs/neetcode.py:
--------------------------------------------------------------------------------
1 | import pathlib
2 | from typing import List
3 | import datetime
4 | from zoneinfo import ZoneInfo
5 |
6 | import discord
7 | import git
8 | from discord import app_commands
9 | from discord.ext import commands, tasks
10 | from git.repo.base import Repo
11 |
12 |
13 |
14 | class Neetcode(commands.Cog):
15 | def __init__(self, bot: commands.Bot) -> None:
16 | self.bot = bot
17 | self.logger = self.bot.logger
18 | self.pull_repo.start()
19 | self.daily_report.start() # Start the daily report loop
20 | self.bot_spam_channels = [
21 | 1053845551315173397, # bot-spam -> abes-server
22 | 1053874909014675576, # bot-playground -> neetcode
23 |
24 | ]
25 |
26 | self.command_usage_stats = {
27 | "pulls": 0,
28 | "leetcode_invoked": 0,
29 | }
30 |
31 | @tasks.loop(time=datetime.time(hour=0, minute=0, tzinfo=ZoneInfo("America/New_York")))
32 | async def daily_report(self):
33 | # Log the daily report
34 | self.logger.info("Daily Report:")
35 | self.logger.info(f"Number of repo pulls: {self.command_usage_stats['pulls']}")
36 | self.logger.info(f"Number of leetcode command invocations: {self.command_usage_stats['leetcode_invoked']}")
37 |
38 | @tasks.loop(time=datetime.time(hour=22, minute=54, tzinfo=ZoneInfo("America/New_York")))
39 | async def pull_repo(self):
40 | o = self.repo.remotes.origin
41 | o.pull()
42 | self.logger.info("pulled repo on timer")
43 | self.command_usage_stats["pulls"] += 1 # Increment pull count
44 |
45 |
46 | async def cog_load(self) -> None:
47 | self.neetcode = pathlib.Path("leetcode")
48 | self.neetcode.mkdir(exist_ok=True)
49 | try:
50 | self.repo = Repo.clone_from(
51 | "https://github.com/neetcode-gh/leetcode.git", self.neetcode
52 | )
53 | self.logger.info("cloned repo")
54 | self.repo = Repo(self.neetcode)
55 | except git.exc.GitCommandError:
56 | self.repo = Repo(self.neetcode)
57 | o = self.repo.remotes.origin
58 | o.pull()
59 | self.logger.info("pulled repo")
60 |
61 | self.languages = [
62 | x.name
63 | for x in self.neetcode.iterdir()
64 | if x.is_dir() and not x.name.startswith(".")
65 | ]
66 |
67 | @app_commands.command()
68 | @app_commands.describe(
69 | number="the number leetcode problem you want a soluiton for",
70 | language="the coding language",
71 | )
72 | async def leetcode(
73 | self, interaction: discord.Interaction, number: int, language: str
74 | ):
75 | self.command_usage_stats["leetcode_invoked"] += 1
76 | # add leading zeros to match file names
77 | number = "{:04d}".format(number)
78 | """Returns the leetcode solution"""
79 | files = list(self.neetcode.glob(language + "/" + str(number) + "-*"))
80 | if language not in self.languages or len(files) == 0:
81 | await interaction.response.send_message(
82 | f"there are no solutions for leetcode problem #{number} in {language}",
83 | ephemeral=True
84 | )
85 | self.logger.info(f"{interaction.user} asked for problom #{number} in {language} but none exist")
86 | return
87 |
88 | self.logger.info(f"{interaction.user} asked for problom #{number} in {language}")
89 | with open(files[0]) as f:
90 | code = f.read()
91 | if interaction.channel_id in self.bot_spam_channels or interaction.channel.name.lower() == "leetcode":
92 | problem_name = pathlib.Path(files[0].stem).name.replace('-', ' ')
93 | await interaction.response.send_message(f"Problem #{problem_name} ({language})\n```{language}\n{code}\n```")
94 | else:
95 | await interaction.response.send_message(f"```{language}\n{code}\n```", ephemeral=True)
96 |
97 | @leetcode.autocomplete("language")
98 | async def leetcode_autocomplete(
99 | self,
100 | interaction: discord.Interaction,
101 | current: str,
102 | ) -> List[app_commands.Choice[str]]:
103 | return [
104 | app_commands.Choice(name=language, value=language)
105 | for language in self.languages
106 | if current.lower() in language.lower()
107 | ]
108 |
109 | @commands.command(hidden=True)
110 | @commands.is_owner()
111 | async def stats(self, ctx):
112 | """Reports the usage stats of the bot."""
113 | stats_message = "Command Usage Stats:\n"
114 | stats_message += f"Number of repo pulls: {self.command_usage_stats['pulls']}\n"
115 | stats_message += f"Number of leetcode command invocations: {self.command_usage_stats['leetcode_invoked']}\n"
116 |
117 | await ctx.send(stats_message)
118 |
119 |
120 |
121 | async def setup(bot: commands.Bot):
122 | await bot.add_cog(Neetcode(bot))
123 |
--------------------------------------------------------------------------------