Compare commits

...
31 Commits
Author SHA1 Message Date
marc eb2f677a0f branches ignore
Docker / build (push) Canceled after 0s
Docker / test-python-versions (macos-latest, 3.10) (push) Canceled after 0s
Docker / test-python-versions (macos-latest, 3.8) (push) Canceled after 0s
Docker / test-python-versions (macos-latest, 3.9) (push) Canceled after 0s
Docker / test-python-versions (ubuntu-latest, 3.10) (push) Canceled after 0s
Docker / test-python-versions (ubuntu-latest, 3.8) (push) Canceled after 0s
Docker / test-python-versions (ubuntu-latest, 3.9) (push) Canceled after 0s
Docker / test-python-versions (windows-latest, 3.10) (push) Canceled after 0s
Docker / test-python-versions (windows-latest, 3.8) (push) Canceled after 0s
Docker / test-python-versions (windows-latest, 3.9) (push) Canceled after 0s
2024-06-18 01:05:29 +02:00
marc 533eba17d2 requirements 2024-06-18 01:03:49 +02:00
marc 7d7a68bc38 no duplicate 2024-06-18 00:58:09 +02:00
marc 9106c08c95 update 2024-06-18 00:56:04 +02:00
marc 3b4bdac5df groq clean 2024-06-18 00:53:04 +02:00
marc 70a11d7216 groq that works 2024-06-18 00:50:49 +02:00
marc b44e832146 audio recording 2024-06-17 15:17:58 +02:00
marc efea057cf2 Merge branch 'master' into alpine 2023-08-16 19:46:39 +02:00
marc 5612ff02b2 docker test pipeline to test docker builds 2023-08-16 19:46:03 +02:00
marc 8e851d890e build 2023-08-16 16:49:19 +02:00
marc c6532aa640 tag and latest ids 2023-08-16 16:48:22 +02:00
marc bc63ee1a04 no need for pipfiles anymore since poetry is used 2023-08-16 16:41:41 +02:00
marc d3be8ea543 update 2023-08-16 16:40:45 +02:00
marc d0724764dd ignore poetry stuff 2023-08-16 16:37:25 +02:00
marc 528651681b alpine 2023-08-16 16:29:38 +02:00
marc 71a696f21d good requirements.txt 2023-08-14 00:45:34 +02:00
marc 0205ce03d8 fix makefile 2023-08-14 00:42:36 +02:00
marc 2898bc2afd fix discord bot with poetry 2023-08-14 00:37:12 +02:00
marc 946c7382e5 Merge branch 'master' into poetry 2023-08-09 08:19:00 +02:00
marc e9e26cbe89 no .python-version pls 2023-08-09 08:18:41 +02:00
marc 5e098345d3 poetry lock changed 2023-08-08 18:49:22 +02:00
marc 57184f0b21 poetry first lock file 2023-08-06 16:04:33 +02:00
marc b8cbe8c80c poetry 2023-08-06 15:43:59 +02:00
marc 9e6a89fe78 update 2023-01-15 18:27:37 +01:00
marc 78d3c42040 update 2023-01-15 18:26:27 +01:00
marc c354ca6362 env var 2023-01-15 18:08:25 +01:00
marc 0306f11305 funding 2022-12-20 17:02:27 +01:00
marc 3668ea7d98 fix 2022-06-01 21:10:06 +02:00
marc 534050a991 update 2022-05-23 01:25:32 +02:00
marc 2893f516dc latest 2022-05-22 00:04:41 +02:00
marc be20e90f08 colorama 2022-05-22 00:03:50 +02:00
22 changed files with 4598 additions and 1576 deletions
+2
View File
@@ -10,3 +10,5 @@ run.sh
runtime.txt
Pipfile
Pipfile.lock
pyproject.toml
poetry.lock
+6
View File
@@ -1 +1,7 @@
liberapay: marcpartensky
patreon: marcpartensky
github: marcpartensky
custom:
# - monero:43J2XmRtjDPRLD35UsYGH2AZamFBQz7dt4VdvZXVm3SuadTrvcoXUXBRUNHqukFte23Q5TWXqhbwaLJJgoVQzzbe2AJDANQ
- https://paypal.me/marcpartensky
- https://buymeacoffee.com/marcpartensky
+8 -2
View File
@@ -32,12 +32,18 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
- name: Build and push with tag
id: tag-build
uses: docker/build-push-action@v2
with:
push: true
tags: marcpartensky/discord-bot:v${{ github.run_number }}.${{ steps.tag.outputs.tag }}
- name: Build and push latest
id: latest-build
uses: docker/build-push-action@v2
with:
push: true
tags: marcpartensky/discord-bot:latest
test-python-versions:
runs-on: ${{ matrix.os }}
+53
View File
@@ -0,0 +1,53 @@
name: Docker
on:
push:
branches-ignore:
- master
env:
IMAGE_NAME: discord-bot
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push latest
id: latest-build
uses: docker/build-push-action@v2
with:
push: false
tags: marcpartensky/discord-bot:latest
test-python-versions:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.8', '3.9', '3.10']
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
cache: pip
- uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ hashFiles('setup.py') }}-${{ hashFiles('requirements.txt') }}
- name: Install requirements
run: pip install -r requirements.txt
- name: Display Python version
run: python -c "import sys; print(sys.version)"
+1
View File
@@ -8,3 +8,4 @@ venv/
.env
.env.dev
./config/marc_store.yml
.python-version
+30 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.8.12-slim
FROM python:3.8.13-slim as ubuntu
LABEL maintainer="marc.partensky@gmail.com"
LABEL image="https://hub.docker.com/r/marcpartensky/discord-bot"
LABEL source="https://github.com/marcpartensky/discord-bot"
@@ -24,3 +24,32 @@ ENV DISCORD_BOT_PORT 8000
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/live || exit 1
ENTRYPOINT ["python", "__main__.py"]
FROM python:3.8.13-alpine as alpine
LABEL maintainer="marc.partensky@proton.me"
LABEL image="https://hub.docker.com/r/marcpartensky/discord-bot"
LABEL source="https://github.com/marcpartensky/discord-bot"
RUN apk update
RUN apk add ffmpeg git gcc g++ gfortran musl musl-dev build-base cmake openblas pkgconfig
# RUN apk add ffmpeg git py3-scikit-learn
COPY requirements.txt ./
RUN pip install -U pip
RUN pip install -r requirements.txt
WORKDIR /app
COPY ./tts tts
COPY ./libs libs
COPY ./assets assets
COPY ./utils utils
COPY ./config config
COPY ./models models
COPY ./cogs cogs
COPY __main__.py LICENSE ./
ENV DISCORD_BOT_HOST 0.0.0.0
ENV DISCORD_BOT_PORT 8000
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/live || exit 1
ENTRYPOINT ["python", "__main__.py"]
+5 -9
View File
@@ -1,8 +1,8 @@
start:
pipenv run python .
poetry run python .
update:
pipenv update
pipenv lock -r > requirements.txt
poetry update
poetry export --output requirements.txt
dev:
docker-compose -f docker-compose.yml up -d --build --force-recreate --remove-orphans dev
prod:
@@ -14,12 +14,8 @@ push: build
deploy: push
DOCKER_HOST=ssh://vps docker service update vps_esclave
setup:
pip install pipenv
pipenv shell
pipenv install
poetry install
poetry shell
recreate:
docker-compose -f docker-compose.yml up --force-recreate dev
fixwithbrew:
brew install python@3.8
sudo ln -sf /home/linuxbrew/.linuxbrew/Cellar/python@3.8/3.8.12_1/bin/python3.8 /usr/bin/python3.8
Generated
-1444
View File
File diff suppressed because it is too large Load Diff
+14 -4
View File
@@ -17,7 +17,7 @@ from dotenv import load_dotenv
load_dotenv()
from config.config import prefix, masters, delete_after_time, status, access
from config.config import prefix, masters, access
from config.credentials import token, client_id
from discord.ext import commands, tasks
@@ -31,6 +31,9 @@ from discord.ext import commands, tasks
# # Notice how you can use spaces in prefixes. Try to keep them simple though.
# prefixes = ['>?', 'lol ', '!?']
# # Check to see if we are outside of a guild. e.g DM's etc.
# if not message.guild:
# # Only allow ? to be used in DMs
@@ -39,8 +42,6 @@ from discord.ext import commands, tasks
# # If we are in a guild, we allow for the user to mention us or use any of the prefixes in our list.
# return commands.when_mentioned_or(*prefixes)(bot, message)
client = commands.Bot(command_prefix=prefix, case_insensitive=True)
client.id = client_id
class Main(commands.Cog):
@@ -56,7 +57,7 @@ class Main(commands.Cog):
self.load_cogs()
self.load_status()
self.load_icloud()
self.api: PycloudService = None
# self.api: PycloudService = None
def load_icloud(self):
"""Charge l'api d'icloud."""
@@ -79,6 +80,7 @@ class Main(commands.Cog):
@commands.command()
async def environment(self, ctx: commands.Context):
"""Affiche l'environnement dans lequel le bot tourne."""
print("environment")
host = os.environ.get("HOST")
await ctx.send(f"> Environment: **{host}**")
@@ -96,6 +98,7 @@ class Main(commands.Cog):
for i, filename in enumerate(cogs):
bar.update(i)
if filename.endswith(".py"):
print(filename)
self.bot.load_extension(f"cogs.{filename[:-3]}")
@commands.command()
@@ -280,5 +283,12 @@ def setup(bot: commands.Bot):
if __name__ == "__main__":
os.system("clear")
print("prefix:", prefix)
intents = discord.Intents.default()
intents.message_content = True
# client = commands.Bot(command_prefix=prefix, case_insensitive=True)
client = commands.Bot(command_prefix=prefix, intents=intents)
client.id = client_id
setup(client)
client.run(token)
+27
View File
@@ -0,0 +1,27 @@
import discord
import os
import dotenv
from discord.ext import commands
dotenv.load_dotenv()
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix=">", intents=intents)
# bot = discord.Bot()
@bot.command()
async def hello(ctx, name: str = None):
name = name or ctx.author.name
await ctx.respond(f"Hello {name}!")
@bot.command(name="Say Hello")
async def hi(ctx, user):
await ctx.respond(f"{ctx.author.mention} says hello to {user.name}!")
@bot.command()
async def ping(ctx):
await ctx.send("pong")
bot.run(os.environ["DISCORD_TOKEN"])
+79
View File
@@ -0,0 +1,79 @@
#!/bin/env python
# from config.config import masters, access, check
# from config import shops
# from utils import tools
from config.config import access, cluster
from discord.ext import commands
import discord
import os
import psycopg2
class Backup(commands.Cog):
def __init__(self, bot):
"""Setup postgres logins"""
self.bot = bot
self.host = os.environ.get("POSTGRES_HOST")
self.port = os.environ.get("POSTGRES_PORT")
self.database = os.environ.get("POSTGRES_DATABASE")
self.user = os.environ.get("POSTGRES_USER")
self.password = os.environ.get("POSTGRES_PASSWORD")
self.color = 0x0064a5
self.connection: psycopg2.connection
self.cursor: psycopg2.cursor
@commands.command(name="postgres-connect")
@access.admin
async def connect_postgres(self, ctx: commands.Context):
"""Change les identifiants postgres avec un string user,password,database,host,port."""
self.connection = psycopg2.connect(
user=self.user,
password=self.password,
host=self.host,
port=self.port,
database=self.database
)
self.cursor = self.connection.cursor()
await ctx.send("Connected to the database")
@commands.command(name="postgres-set")
@access.admin
async def set_postgres_logins(self, ctx: commands.Context, connection: str):
"""Change les identifiants postgres avec un string user,password,database,host,port."""
try:
self.user, self.password, self.database, self.host, self.port = connection.split(",")
except:
return await ctx.send("> Le format doit être le suivant: user,password,database,host,port")
embed = discord.Embed(
title="PostgreSQL logins", color=discord.Color(self.color)
)
embed.add_field(name="user", value=self.user)
embed.add_field(name="password", value=self.password)
embed.add_field(name="database", value=self.database)
embed.add_field(name="host", value=self.host)
embed.add_field(name="port", value=self.port)
await ctx.send(embed=embed)
@commands.command(name="postgres")
@access.admin
async def get_postgres_logins(self, ctx: commands.Context):
"""Affiche les identifiants postgres uniquement si master"""
embed = discord.Embed(
title="PostgreSQL Logins", color=discord.Color(self.color)
)
embed.add_field(name="user", value=self.user)
embed.add_field(name="password", value=self.password)
embed.add_field(name="database", value=self.database)
embed.add_field(name="host", value=self.host)
embed.add_field(name="port", value=self.port)
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Backup(bot))
+3 -6
View File
@@ -1,14 +1,11 @@
from config.config import access, status, delete_after_time, masters, link
from config import emoji
from config.config import access, delete_after_time, link
from utils.date import days, months
from utils import tools
from discord.ext import commands, tasks
from discord.ext import commands
from translate import Translator
from discord.utils import get
from discord import Message, PartialEmoji, Emoji
from bs4 import BeautifulSoup
from discord import Message, PartialEmoji
import requests
import datetime
import discord
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python
"""Use groq"""
import typing
import discord
from discord.ext import commands
from discord.ext.commands.core import has_role
from utils.tools import create_role_if_missing, Capturing
import sys
import asyncio, os
from websockets.sync.client import connect
GROQ_ROLE = "Groq"
class Groq(commands.Cog):
"""Control Groq shells"""
def __init__(self, bot: commands.Bot):
"""Initialize the cog with the bot."""
self.host = "localhost"
self.port = 8765
self.bot = bot
self.color: discord.Color = discord.Color.darker_grey()
self.thumbnail: str = "https://wow.groq.com/wp-content/uploads/2021/04/groq-logo.svg"
self.shells: typing.Dict[int, dict] = {}
# self.timeout = 3600
# @commands.command(name="groq-shells")
# async def groq_shells(self, ctx: commands.Context):
# """List opened shells."""
# if len(self.shells) == 0:
# return await ctx.send("> No shell opened.")
# embed = discord.Embed(title="Shells :", color=self.color)
# embed.set_thumbnail(url=self.thumbnail)
# shell_users = []
# for shell_user in map(self.bot.fetch_user, self.shells.keys()):
# shell_users.append(f"- {await shell_user}")
# embed.description = "\n".join(shell_users)
# await ctx.send(embed=embed)
@commands.Cog.listener(name="on_message")
async def on_message(self, msg: discord.Message):
"""Read all messages"""
if msg.author == self.bot.user:
return
if msg.content.startswith(self.bot.command_prefix):
return
if self.hash(msg) in self.shells:
answer = await self.ask(msg, msg.content)
await msg.channel.send(answer)
# async def cog_common_error(self, ctx, error):
# """Close all shell sessions."""
# for shell in self.shells.values():
# shell.close()
@commands.command()
async def groq(self, ctx: commands.Context):
"""Go in groq mode."""
key = self.hash(ctx)
# Create a role if in guild
if ctx.guild:
await create_role_if_missing(ctx, GROQ_ROLE, self.color)
embed = discord.Embed(color=self.color)
embed.set_thumbnail(url=self.thumbnail)
embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar)
# embed.description = self.shell_path
if ctx.author.id in self.shells:
shell = self.shells[key]
del self.shells[key]
embed.title = "Closed shell"
await ctx.send(embed=embed)
else:
embed.title = "Opened shell"
await ctx.send(embed=embed)
self.shells[key] = dict(messages=[])
def hash(self, ctx):
"""Make a unique hash with channel id and author."""
print(ctx.author.id, ctx.channel.id)
print(self.shells)
return hash((ctx.author.id, ctx.channel.id))
def build_prompt(self, messages: list) -> str:
"""Build prompt."""
prompt = ""
for (me, bot) in messages:
prompt += f"me: {me}\n"
prompt += f"you: {bot}\n"
return prompt
async def ask(self, ctx, message: str) -> str:
"""Return groq answer"""
key = self.hash(ctx)
prompt = self.build_prompt(self.shells[key]["messages"])
prompt += f"me: {message}\n"
prompt += "answer my last message"
with connect(f"ws://{self.host}:{self.port}") as websocket:
websocket.send(prompt)
answer = str(websocket.recv())
print(f"{prompt} => {answer}")
self.shells[key]["messages"].append((message, answer))
return answer
def setup(bot):
"""Setup the Shell cog."""
bot.add_cog(Groq(bot))
+66
View File
@@ -0,0 +1,66 @@
"""
All sound effects you want in one command.
"""
import time
import datetime
import discord
from utils.date import months, days
from config.config import cluster, masters
from discord.ext import commands
class Record(commands.Cog):
"""Record voices."""
def __init__(self, bot: commands.Bot):
"""Define the recorder"""
# self.sounds = cluster.sound_deck.sounds
self.bot = bot
self.connections = {}
# @property
# def music(self):
# return self.bot.get_cog("Music")
# @commands.group(name="son", aliases=["sound", "sn"])
# async def sound(self, ctx: commands.Context):
# """Groupe des commandes pour un son."""
# if not ctx.invoked_subcommand:
# return await ctx.send(
# f"> Commande inexistante."
# f"\n> Écrivez `{ctx.bot.command_prefix}help son` pour voir les commandes disponibles."
# ""
# )
# @sound.command(name="voir", aliases=["v", "show", "see"])
# async def show(self, ctx: commands.Context, *, name: str):
# """Affiche un son."""
# embed = self.embed_sound(name)
# await ctx.send(embed=embed)
@commands.command(name="record")
async def record(self, ctx): # If you're using commands.Bot, this will also work.
"""Enregistre une voix."""
voice = ctx.author.voice
if not voice:
await ctx.respond("You aren't in a voice channel!")
vc = await voice.channel.connect() # Connect to the voice channel the author is in.
self.connections.update({ctx.guild.id: vc}) # Updating the cache with the guild and channel.
vc.start_recording(
discord.sinks.WaveSink(), # The sink type to use.
self.once_done, # What to do once done.
ctx.channel # The channel to disconnect from.
)
await ctx.respond("Started recording!")
def once_done(self, *args):
print("its done", args)
def setup(bot):
bot.add_cog(Record(bot))
+1 -1
View File
@@ -15,7 +15,7 @@ link = "https://discordapp.com/oauth2/authorize?&client_id=703347349623144549&sc
prefix = os.environ.get("DISCORD_PREFIX") or "."
masters = [478552571510915072] # , 219949869220102147]
wolfram = wolframalpha.Client(wolfram_alpha_id)
ialab_bot_url = "https://ialab.marcpartensky.com"
ialab_bot_url = os.environ.get("IALAB_URL") or "https://ialab.marcpartensky.com"
roles = ["@Maître", "@Admin"]
+2
View File
@@ -5,6 +5,7 @@ services:
build:
context: .
dockerfile: ./Dockerfile
target: ubuntu
ports:
- "8000:8000"
env_file:
@@ -21,5 +22,6 @@ services:
build:
context: .
dockerfile: ./Dockerfile
target: alpine
env_file:
- .env
+19 -12
View File
@@ -1,5 +1,4 @@
# from mongocollection import MongoCollection
import discord
from pymongo import MongoClient
from pymongo.database import Database
from pymongo.collection import Collection, ObjectId
@@ -102,7 +101,7 @@ class MongoCluster(MongoClient):
def __getitem__(self, item):
"""Return a mongo database given its key."""
item = super().__getitem__(item)
if isinstance(item, pymongo.database.Database):
if isinstance(item, Database):
return MongoDatabase.from_database(item)
else:
return item
@@ -120,7 +119,7 @@ class MongoDatabase(Database):
def __getitem__(self, item):
"""Return a mongo collection given a key."""
item = super().__getitem__(item)
if isinstance(item, pymongo.collection.Collection):
if isinstance(item, Collection):
return MongoCollection.from_collection(item)
else:
return item
@@ -201,10 +200,10 @@ class MongoCollection(Collection):
else:
self.insert_one(arg, **kwargs)
def insert_many(self, posts, **kwargs):
def insert_many(self, posts):
"""Insert many posts."""
posts = [self.treat(post) for post in posts]
super().insert_many(post)
super().insert_many(posts)
def __contains__(self, id):
return self.seek_one(_id=id) != None
@@ -241,10 +240,9 @@ class MongoCollection(Collection):
def __delitem__(self, id):
"""Delete a post."""
print(id)
print(self.find_one(dict(_id=id)))
# print(self.find_one(dict(_id=id)))
self.delete_one(dict(_id=id))
print(self.find_one(dict(_id=id)))
# print(self.find_one(dict(_id=id)))
def post(self, post):
"""Replace or insert a post."""
@@ -264,12 +262,14 @@ class MongoCollection(Collection):
"""Set a default post."""
if id in self:
post.update(self[id]._dict)
print(1, post)
# print(1, post)
self[id] = post
print(2, self[id])
# print(2, self[id])
def __setattr__(self, att, value):
if att in type(self).keys or att in dir(self):
"""Set attribute to a collection."""
# print(att, value)
if att in type(self).keys or att in dir(self) or att.startswith("_"):
super().__setattr__(att, value)
else:
self.__setitem__(att, value)
@@ -497,7 +497,14 @@ class User(DictObject):
# command = db.commands.find(machin=34, bidule=324)
if __name__ == "__main__":
mongo_url = "mongodb+srv://esclave:esclave@discord-cluster-5ckni.mongodb.net/test"
import os
from dotenv import load_dotenv
load_dotenv()
cluster = os.environ["DISCORD_MONGO_CLUSTER"]
username = os.environ["DISCORD_MONGO_USERNAME"]
password = os.environ["DISCORD_MONGO_PASSWORD"]
mongo_url = f"mongodb+srv://{username}:{password}@{cluster}"
cl = MongoCluster(mongo_url)
# print('cluster over')
esclave = cl.esclave
Generated
+2536
View File
File diff suppressed because it is too large Load Diff
+21 -12
View File
@@ -1,11 +1,17 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[tool.poetry]
name = "discord-bot"
version = "0.1.0"
description = "A discord bot multi purpose."
authors = ["Marc Partensky <marc.partensky@gmail.com>"]
license = "GPL2"
readme = "README.md"
packages = [{include = "discord_bot"}]
[packages]
[tool.poetry.dependencies]
python = "3.8.13"
# python = "3.8-dev"
beautifulsoup4 = "*"
"discord.py[voice]" = "*"
# "discord.py[voice]" = "*"
fbchat = "*"
# fbchat = {git = "git://github.com/carpedm20/fbchat.git"}
nightcore = "*"
@@ -36,11 +42,14 @@ unidecode = '*'
future = '*'
selenium = "*"
fake-useragent = "*"
colorama = "*"
# py-cord = "^2.4.1"
psycopg2 = "*"
py-cord = {extras = ["voice"], version = "^2.5.0"}
groq = "^0.9.0"
websockets = "^12.0"
[dev-packages]
black = "*"
pylint = "*"
jedi = "*"
[requires]
python_version = "3.8.13"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
+1389 -85
View File
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
from enum import Enum
import discord
import os
import dotenv
from discord import Forbidden
dotenv.load_dotenv()
bot = discord.Bot(debug_guilds=["Beta"])
connections = {}
class Sinks(Enum):
mp3 = discord.sinks.MP3Sink()
wav = discord.sinks.WaveSink()
pcm = discord.sinks.PCMSink()
ogg = discord.sinks.OGGSink()
mka = discord.sinks.MKASink()
mkv = discord.sinks.MKVSink()
mp4 = discord.sinks.MP4Sink()
m4a = discord.sinks.M4ASink()
async def finished_callback(sink, channel: discord.TextChannel, *args):
recorded_users = [f"<@{user_id}>" for user_id, audio in sink.audio_data.items()]
await sink.vc.disconnect()
files = [
discord.File(audio.file, f"{user_id}.{sink.encoding}")
for user_id, audio in sink.audio_data.items()
]
await channel.send(
f"Finished! Recorded audio for {', '.join(recorded_users)}.", files=files
)
@bot.command()
async def get_channels(ctx, id):
try:
guild = await bot.get_guild(id)
except Forbidden:
await ctx.send("I do not have access to that guild or it doesn't exist.")
else:
for channel in guild.channels:
print(channel.id)
@bot.command()
async def start(ctx: discord.ApplicationContext, sink: Sinks):
"""Record your voice!"""
voice = ctx.author.voice
if not voice:
return await ctx.respond("You're not in a vc right now")
vc = await voice.channel.connect()
connections.update({ctx.guild.id: vc})
vc.start_recording(
sink.value,
finished_callback,
ctx.channel,
)
await ctx.respond("The recording has started!")
@bot.command()
async def stop(ctx: discord.ApplicationContext):
"""Stop recording."""
if ctx.guild.id in connections:
vc = connections[ctx.guild.id]
vc.stop_recording()
del connections[ctx.guild.id]
await ctx.delete()
else:
await ctx.respond("Not recording in this guild.")
bot.run(os.environ["DISCORD_TOKEN"])
+141
View File
@@ -0,0 +1,141 @@
import io
import pydub # pip install pydub==0.25.1
import discord
from discord.ext import commands
from discord.sinks import MP3Sink
import os
import dotenv
dotenv.load_dotenv()
PREFIX = "."
intents = discord.Intents.default()
intents.members = True
intents.message_content = True
# bot = commands.Bot(
# command_prefix=PREFIX,
# intents=intents,
# )
intents = discord.Intents.default()
intents.message_content = True
# client = commands.Bot(command_prefix=prefix, case_insensitive=True)
bot = commands.Bot(command_prefix=PREFIX, intents=intents)
# setup(client)
# bot = discord.Bot(command_prefix=PREFIX)
@bot.event
async def on_ready():
print(f"Logged in as {bot.user}")
@bot.command()
async def answer(ctx: discord.ApplicationContext):
"""answer"""
await ctx.respond("answer!")
async def finished_callback(sink: MP3Sink, channel: discord.TextChannel):
mention_strs = []
audio_segs: list[pydub.AudioSegment] = []
files: list[discord.File] = []
longest = pydub.AudioSegment.empty()
for user_id, audio in sink.audio_data.items():
mention_strs.append(f"<@{user_id}>")
seg = pydub.AudioSegment.from_file(audio.file, format="mp3")
# Determine the longest audio segment
if len(seg) > len(longest):
audio_segs.append(longest)
longest = seg
else:
audio_segs.append(seg)
audio.file.seek(0)
files.append(discord.File(audio.file, filename=f"{user_id}.mp3"))
for seg in audio_segs:
longest = longest.overlay(seg)
with io.BytesIO() as f:
longest.export(f, format="mp3")
await channel.send(
f"Finished! Recorded audio for {', '.join(mention_strs)}.",
files=files + [discord.File(f, filename="recording.mp3")],
)
@bot.command()
async def join(ctx: discord.ApplicationContext):
"""Join the voice channel!"""
voice = ctx.author.voice
if not voice:
return await ctx.respond("You're not in a vc right now")
await voice.channel.connect()
await ctx.respond("Joined!")
@bot.command()
async def start(ctx: discord.ApplicationContext):
"""Record the voice channel!"""
voice = ctx.author.voice
if not voice:
return await ctx.respond("You're not in a vc right now")
vc: discord.VoiceClient = ctx.voice_client
if not vc:
return await ctx.respond(
"I'm not in a vc right now. Use `/join` to make me join!"
)
vc.start_recording(
MP3Sink(),
finished_callback,
ctx.channel,
sync_start=True, # WARNING: This feature is very unstable and may break at any time.
)
await ctx.respond("The recording has started!")
@bot.command()
async def stop(ctx: discord.ApplicationContext):
"""Stop the recording"""
vc: discord.VoiceClient = ctx.voice_client
if not vc:
return await ctx.respond("There's no recording going on right now")
vc.stop_recording()
await ctx.respond("The recording has stopped!")
@bot.command()
async def leave(ctx: discord.ApplicationContext):
"""Leave the voice channel!"""
vc: discord.VoiceClient = ctx.voice_client
if not vc:
return await ctx.respond("I'm not in a vc right now")
await vc.disconnect()
await ctx.respond("Left!")
bot.run(os.environ["DISCORD_TOKEN"])