-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Main files.
- Loading branch information
Showing
9 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Discord Role Manager Bot | ||
|
||
Role Manager is a discord BOT that utilizes Google Sheets for the organization of a server's hierarchy and permissions. | ||
|
||
|
||
## Details | ||
|
||
This BOT allows for you to export and organize your roles in a Google Sheet in a very user friendly way. | ||
|
||
Three requests are made per usage of !export. One for clearing the spreadsheet, one for updating the roles/permission titles and one for the permission values themselves, making it extremely light and difficult for it to hit the Google Docs quota limit. | ||
## Installation - Add the BOT to your server | ||
|
||
Paste this on your browser to invite the BOT to a server you manage. The BOT requires the Administrator permission. | ||
|
||
```bash | ||
https://discord.com/api/oauth2/authorize?client_id=823014411945377824&permissions=8&scope=bot | ||
``` | ||
|
||
## Usage - BOT Commands | ||
|
||
```python | ||
!setuphelp # Instructions on how to set up the BOT for your server. | ||
|
||
!configure # Add your server to the BOT's database, along with your Google Sheet. | ||
|
||
!export # Export your server's roles and their permissions to your Google Sheet. | ||
``` | ||
|
||
## Requirements (for Developers) | ||
|
||
```python | ||
# Regular Module Imports | ||
from os import path, sys | ||
# Discord API Imports | ||
import discord | ||
from discord.ext import commands | ||
# Google Docs/Sheets API Imports | ||
import gspread | ||
from oauth2client.service_account import ServiceAccountCredentials | ||
from googleapiclient.discovery import build | ||
``` | ||
|
||
## Contributing | ||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. | ||
|
||
## License | ||
[MIT](https://choosealicense.com/licenses/mit/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"type": "service_account", | ||
"project_id": "python-role-management-bot", | ||
"private_key_id": "PRIVATE_ID_HERE", | ||
"private_key": "PRIVATE_KEY_HERE", | ||
"client_email": "CLIENT_EMAIL_HERE", | ||
"client_id": "ID_HERE", | ||
"auth_uri": "https://accounts.google.com/o/oauth2/auth", | ||
"token_uri": "https://oauth2.googleapis.com/token", | ||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", | ||
"client_x509_cert_url": "CLIENT_URL_HERE" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
""" | ||
This file holds functions that return the link of the picture/link requested by the main function. | ||
Mainly created to keep the main file clean from enormous links. | ||
Instead of using a local file and uploading it everytime, it grabs the link. | ||
""" | ||
|
||
|
||
def picture(image_name=None): | ||
images = { | ||
"SUCCESS": "https://i.imgur.com/sp2zmN9.png", | ||
"ERROR": "https://i.imgur.com/lLlHVPq.png", | ||
"GSHEET": "https://i.imgur.com/u9PgNkk.png" | ||
} | ||
return images[image_name] | ||
|
||
|
||
def color(color_type=None): | ||
colors = { | ||
"GREEN": 0x14F200, | ||
"RED": 0xE10E0E | ||
} | ||
return colors[color_type] | ||
|
||
|
||
def link(link_type=None): | ||
links = { | ||
"SPREADSHEET": "https://docs.google.com/spreadsheets/d/", | ||
"SCOPE": ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive"], | ||
"TUTORIAL": "https://i.imgur.com/wG2DgC9.png" | ||
} | ||
return links[link_type] |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
""" | ||
This file holds the functions that handle the requests' bodies, | ||
as well as the data included in them, after they are processed and edited accordingly. | ||
""" | ||
|
||
|
||
def clear_request_body(): | ||
clear_data = {} | ||
return clear_data | ||
|
||
|
||
def titles_request_body(role_names, permission_names): | ||
name_data = [ | ||
{ | ||
"majorDimension": "COLUMNS", | ||
"range": "A2", | ||
"values": [role_names] | ||
}, # Fills the first column with all of the role names. | ||
{ | ||
"majorDimension": "ROWS", | ||
"range": "B1", | ||
"values": [permission_names] | ||
}, | ||
] # Fills the first column with all of the permission names. | ||
|
||
request_body = { | ||
"data": name_data, | ||
"valueInputOption": "RAW" | ||
} | ||
return request_body | ||
|
||
|
||
def values_request_body(permission_values): | ||
permission_data = [] | ||
for i in range(len(permission_values)): # Creates a data list for the request in a smart way, so as to make the size of the request dynamic, depending on the amount of roles and permissions. | ||
permission_data.append({"majorDimension": "ROWS", "range": "B" + str(i + 2), "values": [list(permission_values[i].values())]}) | ||
|
||
request_body = { | ||
"data": permission_data, | ||
"valueInputOption": "RAW" | ||
} | ||
return request_body | ||
|
||
|
||
""" | ||
Converts all the True/False values to ✔ and ❌ for user friendliness. | ||
""" | ||
def permission_values_to_emojis(permission_values, permission_names): | ||
for i in range(len(permission_values)): | ||
if permission_values[i]["administrator"]: | ||
for perm_name in permission_names: | ||
permission_values[i][perm_name] = "✔️" | ||
else: | ||
for perm_name in permission_names: | ||
if permission_values[i][perm_name]: | ||
permission_values[i][perm_name] = "✔️" | ||
else: | ||
permission_values[i][perm_name] = "❌" | ||
return permission_values |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# Regular Module Imports | ||
from os import path, sys | ||
# Discord API Imports | ||
import discord | ||
from discord.ext import commands | ||
# Google Docs/Sheets API Imports | ||
import gspread | ||
from oauth2client.service_account import ServiceAccountCredentials | ||
from googleapiclient.discovery import build | ||
# Assistance Files Imports | ||
from media import * | ||
from request_data import * | ||
|
||
""" Google API Initializations """ | ||
SCOPES = link("SCOPE") | ||
CREDENTIALS = ServiceAccountCredentials.from_json_keyfile_name(path.join(sys.path[0], "credentials.json"), SCOPES) | ||
CLIENT = gspread.authorize(CREDENTIALS) | ||
SERVICE = build("sheets", "v4", credentials=CREDENTIALS) | ||
|
||
""" Discord API Initializations """ | ||
BOT = commands.Bot(command_prefix='!') | ||
|
||
|
||
@BOT.event | ||
async def on_ready(): | ||
print("BOT is ready and running!") | ||
|
||
|
||
""" | ||
!setuphelp - Admin Only | ||
This command sends an embed with direct instructions on how | ||
to get the Role Manager Bot set up and running on a server. | ||
""" | ||
@BOT.command() | ||
@commands.has_permissions(administrator=True) | ||
async def setuphelp(ctx): | ||
embed = discord.Embed(title="Role Manager Setup Tutorial", description="Click the link above for detailed instructions with pictures!", url=link("TUTORIAL"), color=color("GREEN")) | ||
embed.add_field(name="Step 1:", value="Create a Google Sheets Worksheet.", inline=False) | ||
embed.add_field(name="Step 2:", value="Click the Share button on the top right and add this e-mail as an author: ```\n" + CREDENTIALS.service_account_email + "```", inline=False) | ||
embed.add_field(name="Step 3:", value="Select 8 columns and right-click >> Insert 8 Columns in your worksheet.", inline=False) | ||
embed.add_field(name="Step 4:", value="Run the !configure command and link your server with your Google spreadsheet.\n```!configure <WORKSHEET ID>```", inline=False) | ||
embed.add_field(name="Step 5:", value="Export the role permissions onto the Google Sheet using:\n``` !export ```", inline=False) | ||
embed.add_field(name="Finished!", value="The bot is now set up and you can start managing your roles! Make sure you provide a valid Spreadsheet ID, or you will encounter an error!", inline=False) | ||
embed.set_thumbnail(url=picture("GSHEET")) | ||
await ctx.send(embed=embed) | ||
|
||
|
||
""" | ||
!configure - Owner Only | ||
This command creates a file for the server in the database (serverdata) | ||
and stores the Google Worksheet ID inside a .txt file named after the server's ID. | ||
If a file already exists, it prompts the user to update the file instead of reconfiguring it. | ||
""" | ||
@BOT.command() | ||
@commands.has_permissions(administrator=True) | ||
async def configure(ctx, *, spreadsheet_id=None): | ||
if len(spreadsheet_id) == 44: # Ensure input was given and that it is valid. | ||
if ctx.message.author.id == ctx.guild.owner_id: # If the sender is the server owner, proceed. | ||
file_name = str(ctx.guild.id) + ".txt" # The name of the file is that of the server's unique ID. | ||
try: # If the file exists, open and read it and give the link. | ||
with open(path.join(path.dirname(path.realpath(__file__)) + r"\serverdata", file_name), "r+") as server_file: | ||
server_file.truncate(0) | ||
server_file.write(spreadsheet_id) | ||
|
||
embed = discord.Embed(title="You already have a worksheet!", description="Your spreadsheet ID has been updated instead!", color=color("GREEN")) | ||
embed.add_field(name="Your worksheet has been linked! Here's the link: ", value=link("SPREADSHEET") + spreadsheet_id) | ||
embed.set_thumbnail(url=picture("GSHEET")) | ||
await ctx.send(embed=embed) | ||
except FileNotFoundError: # If it doesn't, create it and give the complete link. | ||
with open(path.join(path.dirname(path.realpath(__file__)) + r"\serverdata", file_name), "w+") as server_file: | ||
server_file.write(spreadsheet_id) | ||
|
||
embed = discord.Embed(title="Worksheet Configuration Complete!", description="Your server has been added to the database.", color=color("GREEN")) | ||
embed.add_field(name="Your worksheet has been linked! Here's the link: ", value=link("SPREADSHEET") + spreadsheet_id) | ||
embed.set_thumbnail(url=picture("GSHEET")) | ||
await ctx.send(embed=embed) | ||
except Exception as exception: | ||
print("Server ID:" + ctx.guild.id + "\n Exception:" + str(exception)) | ||
embed = discord.Embed(title="Something went wrong!", description="Please contact the BOT owner on GitHub!", color=color("RED")) | ||
embed.add_field(name="Error code: ", value=str(exception)) | ||
embed.set_thumbnail(url=picture("ERROR")) | ||
await ctx.send(embed=embed) | ||
else: # If the sender is a simple Admin, refuse permission with an error embed. | ||
embed = discord.Embed(title="Access Denied!", description="You have no proper authorization for this command.", color=color("RED")) | ||
embed.add_field(name="This command may only be used by the server owner! ", value='<@' + str(ctx.guild.owner_id) + '>') | ||
embed.set_thumbnail(url=picture("ERROR")) | ||
await ctx.send(embed=embed) | ||
else: # If no valid ID was given, ask for a valid ID and show instructions. | ||
embed = discord.Embed(title="No worksheet ID specified!", description="Please specify a valid worksheet ID.", color=color("RED")) | ||
embed.add_field(name="If want to see how to setup this bot use the command: ", value="```!setuphelp```", inline=False) | ||
embed.set_thumbnail(url=picture("ERROR")) | ||
await ctx.send(embed=embed) | ||
|
||
""" | ||
!export - Owner Only | ||
This command exports all the roles and their permissions | ||
from the Discord Server, organizes them and imports them | ||
to the Google Sheet assigned to that Discord Server. | ||
""" | ||
@BOT.command() | ||
@commands.has_permissions(administrator=True) | ||
async def export(ctx): | ||
file_name = str(ctx.guild.id) + ".txt" | ||
try: | ||
with open(path.join(path.dirname(path.realpath(__file__)) + r"\serverdata", file_name), "r+") as server_file: | ||
spreadsheet_id = server_file.read() | ||
try: | ||
role_list = ctx.guild.roles # Export all the roles from a server. List of role type Objects. | ||
role_list.reverse() | ||
role_names = [role.name for role in role_list] # Get all the role names from the role Objects. | ||
role_permissions = {role: dict(role.permissions) for role in role_list} # Put Roles in a dictionary and their permission_values in sub-dictionaries. | ||
permission_names = list(role_permissions[role_list[0]].keys()) # Get all the permission names. | ||
permission_values = permission_values_to_emojis(list(role_permissions.values()), permission_names) # Get all of the permissions values and convert them to √ or X. | ||
|
||
clear_request = SERVICE.spreadsheets().values().clear(spreadsheetId=spreadsheet_id, range="A1:AH1000", body=clear_request_body()) | ||
titles_request = SERVICE.spreadsheets().values().batchUpdate(spreadsheetId=spreadsheet_id, body=titles_request_body(role_names, permission_names)) | ||
values_request = SERVICE.spreadsheets().values().batchUpdate(spreadsheetId=spreadsheet_id, body=values_request_body(permission_values)) | ||
clear_request.execute() # Clears the spreadsheet. | ||
titles_request.execute() | ||
values_request.execute() # Handling and execution of the requests to the Google API. See request_data.py for more info. | ||
|
||
embed = discord.Embed(title="Permission Export Complete!", description="Your server's role permission_values have been successfully exported!", color=color("GREEN")) | ||
embed.add_field(name="Here's the link to your worksheet: ", value=link("SPREADSHEET") + spreadsheet_id) | ||
embed.set_thumbnail(url=picture("GSHEET")) | ||
await ctx.send(embed=embed) | ||
except Exception as exception: | ||
print("Server ID:" + ctx.guild.id + "\n Exception:" + str(exception)) | ||
embed = discord.Embed(title="Worksheet unavailable!", description="There was an issue trying to access your server's worksheet!", color=color("RED")) | ||
embed.add_field(name="Make sure you have followed the !setuphelp steps correctly. If the issue persists, contact the BOT Owner.", value="```!setuphelp```") | ||
embed.set_thumbnail(url=picture("ERROR")) | ||
await ctx.send(embed=embed) | ||
except FileNotFoundError: # If the file does not exist, prompt user to configure. | ||
embed = discord.Embed(title="No file found!", description="There was an issue trying to import your server's file from the database.", color=color("RED")) | ||
embed.add_field(name="You have to configure your server first. Please try the command !setuphelp for more information.", value="```!setuphelp```") | ||
embed.set_thumbnail(url=picture("ERROR")) | ||
await ctx.send(embed=embed) | ||
|
||
""" | ||
BOT RUN Command that logs in the bot with our credentials. | ||
Has to be in the end of the file. | ||
""" | ||
BOT.run('BOT_TOKEN_HERE') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
SPREADSHEET_ID |