diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index fbdafa5..799361c 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -2,15 +2,49 @@ name: CD on: push: - branches: [main, v2] + branches: [main] + +concurrency: + group: cd-${{ github.ref_name }} + cancel-in-progress: true jobs: + build-and-push: + name: Build and push Docker image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Log in to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + - name: Restore Docker build cache + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Build and push image + uses: docker/build-push-action@v7 + with: + context: . + push: true + tags: | + ${{ secrets.DOCKER_USERNAME }}/botmafieux:latest + ${{ secrets.DOCKER_USERNAME }}/botmafieux:${{ github.sha }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache,mode=max + deploy: name: Deploy to VPS needs: build-and-push runs-on: ubuntu-latest steps: - - name: Copy files to VPS + - name: Copy compose file to VPS uses: appleboy/scp-action@v1 with: host: ${{ secrets.VPS_HOST }} @@ -19,7 +53,6 @@ jobs: port: ${{ secrets.VPS_SSH_PORT }} source: "compose.yaml" target: "/opt/docker/botmafieux" - - name: Deploy on VPS uses: appleboy/ssh-action@v1 with: @@ -28,12 +61,7 @@ jobs: key: ${{ secrets.VPS_SSH_KEY }} port: ${{ secrets.VPS_SSH_PORT }} script: | + set -euo pipefail cd /opt/docker/botmafieux - - docker compose -f compose.yaml --env-file .env down - - docker compose -f compose.yaml --env-file .env build - - docker compose -f compose.yaml --env-file .env up -d - - docker compose -f compose.yaml --env-file .env.prod ps \ No newline at end of file + docker compose -f compose.yaml --env-file .env pull + docker compose -f compose.yaml --env-file .env up -d --remove-orphans diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..ca163b7 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,22 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Run tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.13 + - name: Install dependencies + run: python -m pip install --no-cache-dir -r requirements.txt + - name: Run pytest + run: pytest -q diff --git a/compose.yaml b/compose.yaml index be44ad3..02f519f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,9 +1,22 @@ +networks: + internal: + internal: true + outbound: + external: true + services: botmafieux: - build: - context: . - dockerfile: ./Dockerfile - tags: - - nebulo9/botmafieux:v2 + image: nebulo9/botmafieux:latest container_name: botmafieux - env_file: ./.env \ No newline at end of file + restart: unless-stopped + networks: + - internal + - outbound + env_file: ./.env + volumes: + - ./config:/config + deploy: + resources: + limits: + cpus: '0.25' + memory: '256M' diff --git a/config/hltv.html b/config/hltv.html new file mode 100644 index 0000000..fc7ae8e --- /dev/null +++ b/config/hltv.html @@ -0,0 +1,6778 @@ + + + + + + + + + + + + + + + + + + + + + + + Counter-Strike Matches & livescore | HLTV.org + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+ +
+
+
+
+
+
+
+
+
+
JULY
+
+
+
+
+
Mon
+
Tue
+
Wed
+
Thu
+
Fri
+
Sat
+
Sun
+
+
+
+
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
+
+
+
+
+
+ +
+ +
+
+
+
Star
+
Clear
+
+
+
+
+
+
Ranked
+
Clear
+
+
+
+
+
+
+
Region
+
Clear
+
+
+
+
+
+
+
+
Tournament
+
Clear
+
+
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Set filters
+
+
+
+
+
+
+ +
+
+
+
+

Live Counter-Strike matches

+
+
Sort by: +
+
Event
+
Time
+
+
+ +
+
+
+
+
+
+
+ + + + + + + + + + +
+
+
+ +
+
+
+
Matches for you +
+
Why am I seeing these matches?this?
+ +
+
+
+
+
No matches for you yet.
Click the icon next to a match to pin them for easier access, or check back later
+
+
+
+
+
+

Upcoming Counter-Strike matches

+
+
+
+
+
Sunday - 2026-07-12 +
+ + + + + + + + + + + + + + + + + + +
+
+
Monday - 2026-07-13 +
+ + + + + + + + + + + + + + + +
+
+ + +
Thursday - 2026-07-16 +
+ + + + + + + + + + + + + + + + + + +
+
+
Friday - 2026-07-17 +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
Saturday - 2026-07-18 +
+ + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ + +
+ + + +
+ + + +
+ + +
+ + +
+
+
+
+
+ +
+
+
+
+
+
Now playing
+
Thumbnail for stream
+ +
Russia +
strogo
+
14733 viewers
+
+
+
+
+
Top streams
+
+
All(60)
+
Casters(37)
+
Streamers(15)
+
Organizers(8)
+
+
+
+
Russia +
strogo
+
(14733)
+
+
Ukraine +
inkmate0
+
(4241)
+
+
Vietnam +
500Bros
+
(2869)
+
+
Poland +
Hyper
+
(1344)
+
+
Russia +
Sashacheer
+
(948)
+
+
Czech Republic +
Bladeito
+
(911)
+
+
Ukraine +
aidstar
+
(846)
+
+
Ukraine +
leniniw
+
(813)
+
+
Russia +
almazer
+
(761)
+
+
Other +
tweeday
+
(741)
+
+
Netherlands +
D0cC
+
(651)
+
+
Other +
donaCS
+
(633)
+
+
Canada +
MrHBatt
+
(615)
+
+
Poland +
byali
+
(611)
+
+
Russia +
BreeL
+
(566)
+
+
Russia +
OverDrive
+
(550)
+
+
United Kingdom +
ESL TV
+
(456)
+
+
Russia +
FpSSS
+
(432)
+
+
Russia +
Mikotohero
+
(380)
+
+
France +
KRL
+
(331)
+
+
Ukraine +
leniniw
+
(325)
+
+
Ukraine +
1mpala
+
(299)
+
+
Russia +
floppya
+
(275)
+
+
Czech Republic +
zur1s
+
(272)
+
+
Other +
VanFanTV
+
(204)
+
+
France +
maleK
+
(202)
+
+
United States +
sh1n
+
(193)
+
+
Turkey +
spyhiddentv
+
(177)
+
+
Ukraine +
guthriee
+
(173)
+
+
United Kingdom +
ESL TV
+
(167)
+
+
Poland +
martynkalandrynka
+
(152)
+
+
Sweden +
GeT_RiGhT
+
(147)
+
+
Finland +
p3kko
+
(131)
+
+
France +
NAGU
+
(112)
+
+
Slovakia +
STYKO
+
(106)
+
+
Bulgaria +
Niku
+
(82)
+
+
Czech Republic +
oskar
+
(76)
+
+
United States +
Fragadelphia
+
(77)
+
+
Germany +
kRYSTAL
+
(75)
+
+
Brazil +
mch
+
(74)
+
+
Ukraine +
Maincast
+
(73)
+
+
Romania +
CHANKY
+
(61)
+
+
Poland +
Majki
+
(62)
+
+
United States +
iamfusiion
+
(53)
+
+
United States +
Frag
+
(51)
+
+
Mongolia +
ESN
+
(40)
+
+
Australia +
viridian
+
(29)
+
+
Other +
kleverr
+
(25)
+
+
United States +
blakert
+
(24)
+
+
Ukraine +
Maincast
+
(19)
+
+
United States +
NuTorious
+
(14)
+
+
Other +
Exort 2
+
(14)
+
+
Hungary +
Esport1 TV
+
(11)
+
+
United States +
Floofys
+
(10)
+
+
Russia +
BetBoom
+
(7)
+
+
Ukraine +
Edward
+
(9)
+
+
Kazakhstan +
BRO
+
(5)
+
+
France +
ArthurrrTV
+
(4)
+
+
United Kingdom +
BARDOLPH
+
(3)
+
+
United States +
Frag 2
+
(2)
+
+
+
+
+
+ +
+
+
+ + + + + +
+ + + + + + + \ No newline at end of file diff --git a/config/insults.txt b/config/insults.txt new file mode 100644 index 0000000..71fe0ff --- /dev/null +++ b/config/insults.txt @@ -0,0 +1,27 @@ +Ta gueule. +Qui a demandé ? +Mais tais-toi! +zZzZzZzZ...... +Tout ça ? +On peut se passer du message je pense. +C'est chiant le message là. +Mdr on s'en fout. +Pas lu. +Trop long j'ai décroché. +Il faut quitter le serveur, vous gênez l'espace s'il vous plaît. +Gênant... +Si je devais noter la pertinence du message ce serait genre 1/10. +Tu es ridicule, arrête d'écrire. +Ça a intéréssé quelqu'un ? +Franchement c'était nécessaire d'écrire ça ? +Honestly the most useless thing I read today. +Dommage que les hommes soient cons… +Ta mère mérite la prison pour avoir mis au monde un HOMME. +T'as pas beaucoup de neurones. +Arrêtons de normaliser les hommes svp. +🚮🚮 +Ton message me donne envie de vomir. +J'ai pas ri perso. +Tiens tiens encore une raison de détester les hommes ! +Y'a moyen ton père c'est Macron. +Tu préfères devenir un être humain sympa ou rester un homme ? diff --git a/config/intern.json b/config/intern.json new file mode 100644 index 0000000..00b9506 --- /dev/null +++ b/config/intern.json @@ -0,0 +1 @@ +[{"mentor_id": 264189554582355969, "guild_id": 897157935006900254, "next_roll": "2026-07-22T00:00:00"}] \ No newline at end of file diff --git a/config/intern_takes.csv b/config/intern_takes.csv new file mode 100644 index 0000000..80772b8 --- /dev/null +++ b/config/intern_takes.csv @@ -0,0 +1,22 @@ +target;weight;take +any;1.0;Tu as besoin d'un café ? +any;1.0;Je peux te faire un café si tu veux ? +any;1.0;Tu as besoin d'un thé ? +any;1.0;Je peux te faire un thé si tu veux ? +any;1.0;Tu as besoin d'un matcha latte? +any;1.0;Je peux te faire un matcha latte si tu veux ? +any;1.0;Tu peux m'aider sur un truc ? +any;1.0;Désolé, j'ai pas compris. +any;1.0;Attends, tu peux réexpliquer ? +any;1.0;J'ai fait une bêtise je crois... +nebulo;2.0;J'ai un bug dans mon code tu peux regarder ? +bluebro;2.0;J'ai un bug dans mon code tu peux regarder ? +viebah;2.0;J'ai un bug dans mon code tu peux regarder ? +merdix;2.0;Mon modèle 3D est cassé, je fais quoi ? +goupzy;2.0;Mon modèle 3D est cassé, je fais quoi ? +viebah;2.0;Mon modèle 3D est cassé, je fais quoi ? +goupzy;2.0;Mon modèle 3D est cassé, je fais quoi ? +nino274;2.0;Mon modèle 3D est cassé, je fais quoi ? +cemesah;2.0;J'ai fait une erreur, il faut réimprimer les cartes... +cemesah;2.0;J'ai pas sauvegardé et InDesign a planté... +cemesah;2.0;Tu veux du bouillon ? \ No newline at end of file diff --git a/tests/test_intern_voice.py b/tests/test_intern_voice.py new file mode 100644 index 0000000..493ac52 --- /dev/null +++ b/tests/test_intern_voice.py @@ -0,0 +1,78 @@ +import unittest +from types import SimpleNamespace +from unittest.mock import AsyncMock, patch + +from cogs.intern import InternCog + + +class InternVoiceBehaviorTests(unittest.TestCase): + def make_cog(self, voice_client=None): + cog = InternCog.__new__(InternCog) + cog.bot = SimpleNamespace(voice_clients=[voice_client] if voice_client else []) + cog.data = [{"guild_id": 123, "mentor_id": 456}] + cog.connected_guild_ids = set() + cog.in_voice_channel = False + return cog + + def make_channel(self, channel_id, guild, connect_mock=None, name="test-channel"): + return SimpleNamespace(id=channel_id, guild=guild, connect=connect_mock or AsyncMock(), name=name) + + def make_member(self, member_id): + return SimpleNamespace(id=member_id) + + async def _run_handler(self, cog, before_channel, after_channel, voice_client): + before = SimpleNamespace(channel=before_channel) + after = SimpleNamespace(channel=after_channel) + member = self.make_member(456) + await cog.on_voice_state_update(member, before, after) + + def test_initial_join_only_connects_with_25_percent_chance(self): + cog = self.make_cog() + connect_mock = AsyncMock() + guild = SimpleNamespace(id=123, voice_client=None) + channel = self.make_channel(1, guild, connect_mock=connect_mock) + before = None + after = SimpleNamespace(channel=channel) + member = self.make_member(456) + + with patch("cogs.intern.random.random", return_value=0.1): + import asyncio + asyncio.run(cog.on_voice_state_update(member, before, after)) + connect_mock.assert_awaited_once() + + cog = self.make_cog() + connect_mock = AsyncMock() + guild = SimpleNamespace(id=123, voice_client=None) + channel = self.make_channel(1, guild, connect_mock=connect_mock) + before = None + after = SimpleNamespace(channel=channel) + member = self.make_member(456) + + with patch("cogs.intern.random.random", return_value=0.9): + import asyncio + asyncio.run(cog.on_voice_state_update(member, before, after)) + connect_mock.assert_not_awaited() + + def test_move_always_moves_the_bot_when_connected(self): + guild = SimpleNamespace(id=123, voice_client=None) + voice_client = SimpleNamespace(move_to=AsyncMock(), disconnect=AsyncMock(), guild=guild) + cog = self.make_cog(voice_client=voice_client) + before_channel = self.make_channel(1, guild) + after_channel = self.make_channel(2, guild) + import asyncio + asyncio.run(self._run_handler(cog, before_channel, after_channel, voice_client)) + voice_client.move_to.assert_awaited_once_with(after_channel) + + def test_disconnect_always_disconnects_when_mentor_leaves(self): + guild = SimpleNamespace(id=123, voice_client=None) + voice_client = SimpleNamespace(move_to=AsyncMock(), disconnect=AsyncMock(), guild=guild) + cog = self.make_cog(voice_client=voice_client) + before_channel = self.make_channel(1, guild) + after_channel = None + import asyncio + asyncio.run(self._run_handler(cog, before_channel, after_channel, voice_client)) + voice_client.disconnect.assert_awaited_once_with(force=True) + + +if __name__ == "__main__": + unittest.main()