update README & add functionality
parent
f471094c58
commit
e3f6f307b1
25
README.md
25
README.md
|
@ -1,2 +1,23 @@
|
||||||
# karl-marx-2
|
Karl Marx 2
|
||||||
A poll-based moderation bot to take care of that noisy scallywag in #general
|
===========
|
||||||
|
Karl Marx 2 takes a somewhat different spin on moderation for smaller servers (~10-35 users), handing the controls back to the users themselves. Instead of a single moderator issuing a command to mute, kick or banish a given user, the choice is left up to anyone and everyone present in the channel. Is the user at hand being annoying or spammy enough to be muted? Are they toxic enough to be kicked/banned? We'll take a vote!
|
||||||
|
|
||||||
|
<center><img src="mute_demo.png" width=350/></center>
|
||||||
|
|
||||||
|
I didn't have as much time as I would have liked to work on this, but I after Hack Week I intend to progressively flesh out its functionality beyond just moderation and further improve it.
|
||||||
|
|
||||||
|
Made with love. [Enjoy!](https://discordapp.com/oauth2/authorize?client_id=592852914553487370&permissions=8&scope=bot) :)
|
||||||
|
|
||||||
|
**IMPORTANT:** json storage is still under development.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
1. Clone the repo wherever you want and make sure you have python3 installed with the discord.py package. (If you don't then just `pip3 install discord.py` and you should be good to go!)
|
||||||
|
2. `python3 bot.py`
|
||||||
|
|
||||||
|
Commands
|
||||||
|
--------
|
||||||
|
- `>>mute` - Hold a 30-second vote to mute a user for 10 minutes. You can set different values in `config.json`.
|
||||||
|
- `>>exile` - Euphamism for banning a user, I guess. By default, the vote lasts 3 hours, and requires that there be at least 10 votes and a 50% majority. Like the `>>mute` command, you can also tweak settings in `config.json`.
|
||||||
|
- `>>anthem` - First verse of the Soviet National Anthem. Might need that at some point to show your communist pride.
|
||||||
|
- `>>ping` - Fun leftovers from when I was first setting up the bot. :P
|
||||||
|
|
94
bot.py
94
bot.py
|
@ -1,7 +1,18 @@
|
||||||
|
"""
|
||||||
|
Karl Marx 2
|
||||||
|
A bot by turtlebasket
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
|
MUTE_VOTE_TIME = 10
|
||||||
|
MUTE_VOTER_MIN = 2
|
||||||
|
MUTE_TIME = 600 # 10 mins
|
||||||
|
BAN_VOTE_TIME = 8
|
||||||
|
BAN_VOTER_MIN = 4
|
||||||
bot = commands.Bot(command_prefix='>>')
|
bot = commands.Bot(command_prefix='>>')
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
|
@ -22,38 +33,36 @@ async def anthem(ctx):
|
||||||
command: anthem
|
command: anthem
|
||||||
some people just need a reference, ya know?
|
some people just need a reference, ya know?
|
||||||
"""
|
"""
|
||||||
|
await ctx.send("Soyuz nyerushimyiy ryespublik svobodnyikh\n"
|
||||||
|
"Splotila navyeki Vyelikaya Rus’.\n"
|
||||||
|
"Da zdravstvuyet sozdannyiy volyey narodov\n"
|
||||||
|
"Yedinyiy, moguchiy Sovyetskiy Soyuz!\n")
|
||||||
|
|
||||||
await ctx.send(
|
# not a command, just some functionality that's used across commands
|
||||||
"Soyuz nyerushimyiy ryespublik svobodnyikh\n"
|
async def take_vote(ctx, question:str, wait_time):
|
||||||
"Splotila navyeki Vyelikaya Rus’.\n"
|
|
||||||
"Da zdravstvuyet sozdannyiy volyey narodov\n"
|
|
||||||
"Yedinyiy, moguchiy Sovyetskiy Soyuz!\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
@bot.command()
|
|
||||||
async def mute(ctx, target_user:str):
|
|
||||||
"""
|
"""
|
||||||
command: soft_mute
|
take_vote(ctx, question:str) - Collects votes
|
||||||
The actual good stuff.
|
ctx: pass from command function
|
||||||
If someone's spamming or being annoying, just mute 'em. Requires 3 votes by default.
|
question: what to ask
|
||||||
|
|
||||||
|
returns [<all who want>, <all who don't want>].
|
||||||
|
It's up to the context/use case to decide how these should be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
votey_message = await ctx.send(
|
votey_message = await ctx.send("**=== NEW VOTE ===**\n{}".format(question))
|
||||||
"=== NEW MUTE VOTE ===\n"
|
|
||||||
"Mute {}?".format(str(target_user)))
|
|
||||||
|
|
||||||
await votey_message.add_reaction('✅')
|
await votey_message.add_reaction('✅')
|
||||||
await votey_message.add_reaction('❌')
|
await votey_message.add_reaction('❌')
|
||||||
|
|
||||||
sleep(10)
|
await asyncio.sleep(wait_time)
|
||||||
|
|
||||||
finished_votey = await votey_message.channel.fetch_message(votey_message.id)
|
finished_votey = await votey_message.channel.fetch_message(votey_message.id)
|
||||||
print(finished_votey.reactions)
|
|
||||||
all_in_favor = 0
|
all_in_favor = 0
|
||||||
not_in_favor = 0
|
not_in_favor = 0
|
||||||
|
|
||||||
for reaction in finished_votey.reactions:
|
for reaction in finished_votey.reactions:
|
||||||
if str(reaction.emoji) == '✅':
|
if str(reaction.emoji) == '✅':
|
||||||
all_in_favor += reaction.count-1
|
all_in_favor += reaction.count-1 # don't include bot's reaction
|
||||||
elif str(reaction.emoji) == '✅':
|
elif str(reaction.emoji) == '✅':
|
||||||
not_in_favor += reaction.count-1
|
not_in_favor += reaction.count-1
|
||||||
else:
|
else:
|
||||||
|
@ -63,11 +72,50 @@ async def mute(ctx, target_user:str):
|
||||||
"**=== VOTE RESULTS ===**\n"
|
"**=== VOTE RESULTS ===**\n"
|
||||||
"✅ - {0}\n"
|
"✅ - {0}\n"
|
||||||
"❌ - {1}\n".format(all_in_favor, not_in_favor))
|
"❌ - {1}\n".format(all_in_favor, not_in_favor))
|
||||||
|
return [all_in_favor, not_in_favor]
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def mute(ctx, target_user:discord.User):
|
||||||
|
|
||||||
|
results = await take_vote(ctx, "Mute `{}`?".format(target_user), MUTE_VOTE_TIME)
|
||||||
|
all_in_favor = results[0]
|
||||||
|
not_in_favor = results[1]
|
||||||
|
|
||||||
|
if all_in_favor > 0 and all_in_favor - not_in_favor > 0:
|
||||||
|
# Take action to mute user
|
||||||
|
|
||||||
|
# add temp. role for mute
|
||||||
|
muted_role = await ctx.guild.create_role(name="Muted")
|
||||||
|
# edit role position to take precedence over other roles
|
||||||
|
await muted_role.edit(position=ctx.guild.get_member(target_user.id).top_role.position+1)
|
||||||
|
|
||||||
|
for channel in ctx.guild.channels:
|
||||||
|
await channel.set_permissions(muted_role, read_messages=True, send_messages=False, add_reactions=False, connect=False)
|
||||||
|
|
||||||
|
await ctx.guild.get_member(target_user.id).add_roles(muted_role)
|
||||||
|
await ctx.send("**{0}, the majority has ruled that you should be muted.** See ya in {1} minutes!".format(target_user, int(MUTE_TIME/60)))
|
||||||
|
await asyncio.sleep(MUTE_TIME)
|
||||||
|
await muted_role.delete()
|
||||||
|
|
||||||
if all_in_favor > 2 and all_in_favor - not_in_favor > 0:
|
|
||||||
await ctx.send("{} **has** been muted.".format(target_user))
|
|
||||||
else:
|
else:
|
||||||
await ctx.send("{} **has not** been muted.".format(target_user))
|
await ctx.send("**{} has not been muted.**".format(target_user))
|
||||||
print("all in favor: {}".format(all_in_favor))
|
|
||||||
|
@bot.command()
|
||||||
|
async def roletest(ctx):
|
||||||
|
await ctx.send("`{}`".format(ctx.guild.roles))
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def exile(ctx, target_user:discord.User):
|
||||||
|
results = await take_vote(ctx, "Ban `{}`?".format(target_user), BAN_VOTE_TIME)
|
||||||
|
all_in_favor = results[0]
|
||||||
|
not_in_favor = results[1]
|
||||||
|
|
||||||
|
if all_in_favor > not_in_favor and all_in_favor >= BAN_VOTER_MIN: # change to 10 later
|
||||||
|
await ctx.guild.ban(target_user)
|
||||||
|
await ctx.send(":crab: :crab: `{}` IS GONE :crab: :crab:".format(target_user.name))
|
||||||
|
elif all_in_favor <= all_in_favor:
|
||||||
|
await ctx.send("The majority (>50%) did not decide on banning `{}`.".format(target_user.name))
|
||||||
|
elif all_in_favor < 1:
|
||||||
|
await ctx.send("Not enough users voted to ban `{}`.".format(target_user.name))
|
||||||
|
|
||||||
bot.run(open("token.txt").read().strip())
|
bot.run(open("token.txt").read().strip())
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Loading…
Reference in New Issue