Skip to content

Commit 61b68db

Browse files
authored
Merge pull request #292 from allthingslinux/mailcow
feat: add Mailcow integration for user mail registration
2 parents de70280 + d7308dc commit 61b68db

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,7 @@ GITHUB_CLIENT_SECRET=
3939
GITHUB_PUBLIC_KEY=
4040
GITHUB_INSTALLATION_ID=
4141
GITHUB_PRIVATE_KEY_BASE64=
42+
43+
# Mailcow
44+
MAILCOW_API_KEY=
45+
MAILCOW_API_URL=

tux/cogs/admin/mail.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import discord
2+
import httpx
3+
from discord import app_commands
4+
from discord.ext import commands
5+
from loguru import logger
6+
7+
from tux.utils.constants import Constants as CONST
8+
9+
10+
class Mail(commands.Cog):
11+
def __init__(self, bot: commands.Bot):
12+
self.bot = bot
13+
self.api_url = CONST.MAILCOW_API_URL
14+
self.headers = {
15+
"Content-Type": "application/json",
16+
"Accept": "application/json",
17+
"X-API-Key": CONST.MAILCOW_API_KEY,
18+
"Authorization": f"Bearer {CONST.MAILCOW_API_KEY}",
19+
}
20+
self.default_options = {
21+
"active": "1",
22+
"domain": "atl.tools",
23+
"password": "changeme1234!",
24+
"password2": "changeme1234!",
25+
"quota": "3072",
26+
"force_pw_update": "1",
27+
"tls_enforce_in": "0",
28+
"tls_enforce_out": "0",
29+
"tags": ["discord_member"],
30+
}
31+
32+
mail = app_commands.Group(name="mail", description="Mail commands.")
33+
34+
@mail.command(name="register", description="Registers a user for mail.")
35+
@app_commands.checks.has_any_role("Root", "Admin", "Mod")
36+
async def register(
37+
self, interaction: discord.Interaction, member: discord.Member, username: str
38+
) -> None:
39+
if interaction.guild:
40+
mailbox_data = self.default_options.copy()
41+
mailbox_data["local_part"] = username
42+
mailbox_data["name"] = username
43+
# Ensure tags are copied correctly and member ID is added
44+
tags = (
45+
self.default_options["tags"]
46+
if isinstance(self.default_options["tags"], list)
47+
else []
48+
)
49+
tags = tags.copy() # Ensure it's a fresh copy of the list
50+
tags.append(str(member.id))
51+
mailbox_data["tags"] = tags
52+
53+
api_path = "/add/mailbox"
54+
api_endpoint = self.api_url + api_path
55+
56+
async with httpx.AsyncClient(timeout=10.0) as client:
57+
try:
58+
response = await client.post(
59+
api_endpoint, headers=self.headers, json=mailbox_data
60+
)
61+
if response.status_code == 200:
62+
result = response.json()
63+
logger.info(f"Response JSON: {result}")
64+
65+
# Initialize the mailbox_info as 'Unknown'
66+
mailbox_info = "Unknown"
67+
68+
# Check the response for success or failure messages
69+
for item in result:
70+
if "msg" in item:
71+
if "mailbox_added" in item["msg"]:
72+
mailbox_info = item["msg"][1]
73+
break
74+
if "mailbox_quota_left_exceeded" in item["msg"]:
75+
await interaction.response.send_message(
76+
"Failed to register the mailbox. Quota limit exceeded.",
77+
ephemeral=True,
78+
)
79+
return
80+
81+
await interaction.response.send_message(
82+
f"Successfully registered {mailbox_info} for mail.", ephemeral=True
83+
)
84+
85+
dm_message = f"""
86+
**Your mailbox has been successfully registered!**
87+
88+
**Email Address**: `{mailbox_info}`
89+
**Access Mailbox**: [mail.atl.tools](https://mail.atl.tools)
90+
**Default Password**: `changeme1234!`
91+
92+
**Please change your password after logging in for the first time.**
93+
94+
After changing, you can also set up your mailbox on your mobile device or email client following the instructions provided on the mail server. Alternatively, feel free to use our webmail interface available at [mail.atl.tools/SOGo](https://mail.atl.tools/SOGo/).
95+
96+
If you have any questions or need assistance, please feel free to reach out to the server staff. Enjoy your new mailbox! 📬
97+
"""
98+
99+
try:
100+
await member.send(
101+
f"Hello {member.mention},\n{dm_message.strip()}",
102+
suppress_embeds=True,
103+
)
104+
except discord.Forbidden:
105+
await interaction.response.send_message(
106+
f"Failed to send a DM to {member.mention}. Please enable DMs from server members.",
107+
ephemeral=True,
108+
)
109+
110+
elif response.status_code == 401:
111+
await interaction.response.send_message(
112+
"Unauthorized. Check your API credentials.", ephemeral=True
113+
)
114+
else:
115+
await interaction.response.send_message(
116+
f"Failed to register {username} for mail. Status code: {response.status_code}.",
117+
ephemeral=True,
118+
)
119+
except httpx.RequestError as exc:
120+
await interaction.response.send_message(
121+
f"An error occurred while requesting {exc.request.url!r}.", ephemeral=True
122+
)
123+
logger.error(f"An error occurred while requesting, {exc}")
124+
else:
125+
await interaction.response.send_message(
126+
"This command can only be used in a guild (server).", ephemeral=True
127+
)
128+
129+
130+
async def setup(bot: commands.Bot):
131+
await bot.add_cog(Mail(bot))

tux/utils/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class Constants:
4545
"utf-8"
4646
)
4747

48+
# Mailcow constants
49+
MAILCOW_API_KEY: Final[str] = os.getenv("MAILCOW_API_KEY", "")
50+
MAILCOW_API_URL: Final[str] = os.getenv("MAILCOW_API_URL", "")
51+
4852
# Channel constants
4953
LOG_CHANNELS: Final[dict[str, int]] = config["LOG_CHANNELS"].copy()
5054
if DEV and DEV.lower() == "true":

0 commit comments

Comments
 (0)