mirror of
https://github.com/alterware/aw-bot.git
synced 2025-10-26 06:05:54 +00:00
maint: push updates from other repo
This commit is contained in:
10
.github/workflows/docker-publish.yml
vendored
10
.github/workflows/docker-publish.yml
vendored
@@ -14,13 +14,13 @@ jobs:
|
||||
if: github.ref_type == 'tag'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.4.0
|
||||
uses: docker/setup-buildx-action@v3.6.1
|
||||
|
||||
- name: Log in to DockerHub
|
||||
uses: docker/login-action@v3.2.0
|
||||
uses: docker/login-action@v3.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -36,10 +36,10 @@ jobs:
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v6.3.0
|
||||
uses: docker/build-push-action@v6.7.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
3
.github/workflows/lint.yml
vendored
3
.github/workflows/lint.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Install pip
|
||||
run: |
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
pip install flake8
|
||||
|
||||
- name: Run flake8
|
||||
run: |
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
# AlterWare's Discord Bot
|
||||
This repo contains the AlterWare Bot's source code. It is written in Python using discord.py
|
||||
This repo contains the AlterWare Bot written in python using discord.py
|
||||
|
||||
Contributions are welcome! Please follow the guidelines below:
|
||||
|
||||
- Sign [AlterWare CLA](https://alterware.dev/cla) and send a pull request or email your patch at patches@alterware.dev
|
||||
- Make sure that PRs have only one commit, and deal with one issue only
|
||||
|
||||
197
aw.py
197
aw.py
@@ -1,7 +1,8 @@
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Literal
|
||||
|
||||
import discord
|
||||
@@ -22,6 +23,7 @@ ALLOWED_CHANNELS = [
|
||||
1145459788151537804,
|
||||
1145469106133401682,
|
||||
1117540484085194833,
|
||||
1112049391482703873,
|
||||
]
|
||||
GENERAL_CHANNEL = 1110531063744303138
|
||||
|
||||
@@ -36,6 +38,10 @@ bot = commands.Bot(command_prefix="!", intents=discord.Intents.all())
|
||||
tree = bot.tree
|
||||
|
||||
|
||||
def aware_utcnow():
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
def fetch_api_data():
|
||||
response = requests.get("https://api.getserve.rs/v1/servers/alterware")
|
||||
if response.status_code == 200:
|
||||
@@ -146,8 +152,84 @@ async def on_tree_error(
|
||||
bot.tree.on_error = on_tree_error
|
||||
|
||||
|
||||
async def detect_ghost_ping(message):
|
||||
if not message.mentions:
|
||||
return
|
||||
|
||||
channel = bot.get_channel(BOT_LOG)
|
||||
if channel:
|
||||
embed = discord.Embed(
|
||||
title="Ghost Ping",
|
||||
description="A ghost ping was detected.",
|
||||
color=0xDD2E44,
|
||||
)
|
||||
embed.add_field(
|
||||
name="Author", value=message.author.mention, inline=True
|
||||
) # noqa
|
||||
embed.add_field(
|
||||
name="Channel", value=message.channel.mention, inline=True
|
||||
) # noqa
|
||||
|
||||
mentioned_users = ", ".join([user.name for user in message.mentions])
|
||||
embed.add_field(
|
||||
name="Mentions",
|
||||
value=f"The message deleted by {message.author} mentioned: {mentioned_users}", # noqa
|
||||
inline=False,
|
||||
) # noqa
|
||||
|
||||
embed.set_footer(
|
||||
text=f"Message ID: {message.id} | Author ID: {message.author.id}"
|
||||
)
|
||||
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
async def detect_ghost_ping_in_edit(before, after):
|
||||
before_mentions = set(before.mentions)
|
||||
after_mentions = set(after.mentions)
|
||||
|
||||
if before_mentions == after_mentions:
|
||||
return
|
||||
|
||||
added_mentions = after_mentions - before_mentions
|
||||
removed_mentions = before_mentions - after_mentions
|
||||
|
||||
response = "The mentions in the message have been edited.\n"
|
||||
if added_mentions:
|
||||
response += f"Added mentions: {', '.join(user.name for user in added_mentions)}\n" # noqa
|
||||
if removed_mentions:
|
||||
response += f"Removed mentions: {', '.join(user.name for user in removed_mentions)}" # noqa
|
||||
|
||||
channel = bot.get_channel(BOT_LOG)
|
||||
if channel:
|
||||
embed = discord.Embed(
|
||||
title="Ghost Ping",
|
||||
description="A ghost ping was detected.",
|
||||
color=0xDD2E44,
|
||||
)
|
||||
embed.add_field(name="Author", value=before.author.mention, inline=True) # noqa
|
||||
embed.add_field(
|
||||
name="Channel", value=before.channel.mention, inline=True
|
||||
) # noqa
|
||||
|
||||
embed.add_field(
|
||||
name="Mentions",
|
||||
value=response,
|
||||
inline=False,
|
||||
) # noqa
|
||||
|
||||
embed.set_footer(
|
||||
text=f"Message ID: {before.id} | Author ID: {before.author.id}"
|
||||
)
|
||||
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message_delete(message):
|
||||
if message.author == bot.user:
|
||||
return
|
||||
|
||||
channel = bot.get_channel(BOT_LOG)
|
||||
if channel:
|
||||
embed = discord.Embed(
|
||||
@@ -163,10 +245,23 @@ async def on_message_delete(message):
|
||||
) # noqa
|
||||
if message.content:
|
||||
embed.add_field(name="Content", value=message.content, inline=False) # noqa
|
||||
|
||||
if message.reference is not None:
|
||||
original_message = await message.channel.fetch_message(
|
||||
message.reference.message_id
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name="Replied",
|
||||
value=original_message.author.mention,
|
||||
inline=False, # noqa
|
||||
) # noqa
|
||||
|
||||
embed.set_footer(
|
||||
text=f"Message ID: {message.id} | Author ID: {message.author.id}"
|
||||
)
|
||||
|
||||
await detect_ghost_ping(message)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
@@ -191,12 +286,40 @@ async def on_bulk_message_delete(messages):
|
||||
name="Content", value=message.content, inline=False
|
||||
) # noqa
|
||||
embed.set_footer(
|
||||
text=f"Message ID: {message.id} | Author ID: {message.author.id}" # noqa
|
||||
text=f"Message ID: {message.id} | Author ID: {message.author.id}" # noqa
|
||||
)
|
||||
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message_edit(before, after):
|
||||
channel = bot.get_channel(BOT_LOG)
|
||||
if channel:
|
||||
if not before.content:
|
||||
return
|
||||
|
||||
if after.content and after.content == before.content:
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Edited Message",
|
||||
description="A message was edited.",
|
||||
color=0xDD2E44,
|
||||
)
|
||||
embed.add_field(name="Author", value=before.author.mention, inline=True) # noqa
|
||||
embed.add_field(
|
||||
name="Channel", value=before.channel.mention, inline=True
|
||||
) # noqa
|
||||
embed.add_field(name="Content", value=before.content, inline=False) # noqa
|
||||
embed.set_footer(
|
||||
text=f"Message ID: {before.id} | Author ID: {before.author.id}"
|
||||
)
|
||||
|
||||
await detect_ghost_ping_in_edit(before, after)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if message.author == bot.user:
|
||||
@@ -204,13 +327,13 @@ async def on_message(message):
|
||||
|
||||
# Too many mentions
|
||||
if len(message.mentions) >= 3:
|
||||
await message.delete()
|
||||
member = message.guild.get_member(message.author.id)
|
||||
if member:
|
||||
# Timeout the member for 60 seconds
|
||||
# Timeout the member
|
||||
await member.timeout_for(
|
||||
discord.utils.utcnow() + datetime.timedelta(seconds=60)
|
||||
discord.utils.utcnow() + datetime.timedelta(minutes=5)
|
||||
)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
# Auto delete torrent if post in chat.
|
||||
@@ -226,14 +349,70 @@ async def on_message(message):
|
||||
# print('Checking for patterns...')
|
||||
for pattern in patterns:
|
||||
if re.search(pattern["regex"], message.content, re.IGNORECASE):
|
||||
# print('Checking message content:', message.content, re.IGNORECASE) # noqa
|
||||
# print('Matching pattern regex:', pattern['regex']) # noqa
|
||||
# print('Pattern match:', re.search(pattern['regex'], message.content, re.IGNORECASE)) # noqa
|
||||
response = pattern["response"]
|
||||
await message.channel.send(response)
|
||||
reply_message = await message.reply(response, mention_author=True)
|
||||
await reply_message.add_reaction("\U0000274C")
|
||||
break
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_reaction_add(reaction, user):
|
||||
if reaction.emoji != "\U0000274C":
|
||||
return
|
||||
|
||||
if reaction.message.author != bot.user:
|
||||
return
|
||||
|
||||
current_time = aware_utcnow()
|
||||
time_difference = current_time - reaction.message.created_at
|
||||
if time_difference > timedelta(minutes=5):
|
||||
return
|
||||
|
||||
if reaction.message.reference is None:
|
||||
return
|
||||
|
||||
original_message = await reaction.message.channel.fetch_message(
|
||||
reaction.message.reference.message_id
|
||||
)
|
||||
|
||||
if original_message.author == user:
|
||||
await reaction.message.delete()
|
||||
|
||||
|
||||
def generate_random_nickname():
|
||||
random_number = random.randint(1, 99)
|
||||
return f"Unknown Soldier {random_number:02d}"
|
||||
|
||||
|
||||
def is_valid_username(username):
|
||||
pattern = r"^[\d\x00-\x7F\xC0-\xFF]{2,}"
|
||||
return bool(re.match(pattern, username))
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_member_join(member):
|
||||
name_to_check = member.name
|
||||
|
||||
if member.display_name:
|
||||
name_to_check = member.display_name
|
||||
|
||||
if len(name_to_check) < 3 or not is_valid_username(name_to_check):
|
||||
new_nick = generate_random_nickname()
|
||||
await member.edit(nick=new_nick)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_member_update(before, after):
|
||||
name_to_check = after.name
|
||||
|
||||
if after.nick:
|
||||
name_to_check = after.nick
|
||||
|
||||
if len(name_to_check) < 3 or not is_valid_username(name_to_check):
|
||||
new_nick = generate_random_nickname()
|
||||
await after.edit(nick=new_nick)
|
||||
|
||||
|
||||
# Update Player Counts from API
|
||||
@tasks.loop(minutes=10)
|
||||
async def update_status():
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
"regex": "(?:do\\s+|if\\s+i\\s+don't\\s+)?(?:i|you)?\\s*(?:really\\s+)?(?:need\\s+to|have\\s+to|must|gotta|can)\\s+own\\s+(?:an?\\s+)?(?:official\\s+)?(?:copy\\s+of\\s+)?(?:the\\s+|this\\s+|said\\s+|these\\s+|those\\s+)?(?:cod\\s+)?games?\\s*(?:on\\s+steam|on\\s+alterware)?",
|
||||
"response": "Yes, it is required to own a copy of the game to utilize our clients"
|
||||
},
|
||||
{
|
||||
"regex": "own(ing)* the game",
|
||||
"response": "Yes, it is required to own a copy of the game to utilize our clients"
|
||||
},
|
||||
{
|
||||
"regex": "^.*?\\bbuy\\b.*?\\btwice\\b.*?$",
|
||||
"response": "There is no stipulation in the law that entitles you to a free PC copy of the game if you bought it on another platform"
|
||||
},
|
||||
{
|
||||
"regex": "^.*?\\bbuying\\b.*?\\btwice\\b.*?$",
|
||||
"response": "There is no stipulation in the law that entitles you to a free PC copy of the game if you bought it on another platform"
|
||||
},
|
||||
{
|
||||
"regex": "^.*?\\bbought\\b.*?\\btwice\\b.*?$",
|
||||
"response": "There is no stipulation in the law that entitles you to a free PC copy of the game if you bought it on another platform"
|
||||
},
|
||||
{
|
||||
"regex": "\bhow can I open the console ingame\b",
|
||||
"response": "Press the tilde key (~) or grave key (`) to enter console.\n https://i.imgur.com/drPaC0f.png"
|
||||
@@ -11,8 +27,36 @@
|
||||
"regex": "(.*)(HOW|WHERE)(.*)(CHANGE|MODIFY)(.*)(USERNAME|NAME|IGN)(.*)",
|
||||
"response": "``/name WhateverNameYouWant`` on the in-game console.\nYou open the in-game console with the ~ or ` key (underneath ESC). If you're on a 60% keyboard it's Fn+ESC.\nhttps://i.imgur.com/drPaC0f.png"
|
||||
},
|
||||
{
|
||||
"regex": "^.*?\\bchange\\b.*?\\b(username|name)\\b.*?$",
|
||||
"response": "``/name WhateverNameYouWant`` on the in-game console.\nYou open the in-game console with the ~ or ` key (underneath ESC). If you're on a 60% keyboard it's Fn+ESC.\nhttps://i.imgur.com/drPaC0f.png"
|
||||
},
|
||||
{
|
||||
"regex": "online profile is invalid",
|
||||
"response": "Close your game, locate your Modern Warfare 2 game folder, and delete the `players` folder"
|
||||
},
|
||||
{
|
||||
"regex": "vac ban",
|
||||
"response": "The short answer is no. Our CoD clients are completely external to Steam and Steam servers (you don not play with Steam users). It is impossible to get VAC banned. The servers are completely separate from Steam. You are at no risk of receiving a VAC ban"
|
||||
},
|
||||
{
|
||||
"regex": "do i need Steam",
|
||||
"response": "Yes, it is required to own a copy of the game on Steam to utilize our clients"
|
||||
},
|
||||
{
|
||||
"regex": "unlock(\\s)*all",
|
||||
"response": "Just use the 'Unlock All' button in the barracks menu to get 10th prestige level 70 with everything unlocked. On S1 and IW6, you must type `unlockstats` in either the in-game console or the external console"
|
||||
},
|
||||
{
|
||||
"regex": "unlock everything",
|
||||
"response": "Just use the 'Unlock All' button in the barracks menu to get 10th prestige level 70 with everything unlocked. On S1 and IW6, you must type `unlockstats` in either the in-game console or the external console"
|
||||
},
|
||||
{
|
||||
"regex": "^.*?\\b(get|buy)\\b.*?\\b(dlc|dlcs)\\b.*?$",
|
||||
"response": "We ask all our users to purchase the DLCs on Steam. If you're looking for instructions on how to get the official IW4x DLC map packs, please refer to this forum post: https://forum.alterware.dev/t/how-to-download-bonus-content-iw4x/901"
|
||||
},
|
||||
{
|
||||
"regex": "steam(\\s)*deck",
|
||||
"response": "Are you wondering how to install our clients on the Steam Deck? Check out this guide: https://forum.alterware.dev/t/playing-alterware-on-linux-guide-desktop-handheld/1036"
|
||||
}
|
||||
]
|
||||
@@ -1,3 +1,2 @@
|
||||
discord.py
|
||||
flake8
|
||||
requests
|
||||
|
||||
Reference in New Issue
Block a user