Skip to content

Commit

Permalink
Screens (#56)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dublUayaychtee <[email protected]>
Co-authored-by: dublUayaychtee <[email protected]>
Co-authored-by: kredcool <[email protected]>
  • Loading branch information
5 people authored Jan 4, 2024
1 parent 5841305 commit 178e959
Show file tree
Hide file tree
Showing 79 changed files with 4,345 additions and 95 deletions.
40 changes: 34 additions & 6 deletions .github/workflows/admin_bot-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,19 @@ jobs:
# args: "AdminBot Pytest failed!"

build-and-publish-image:
name: Build and Publish Registry Image
name: Build Registry
runs-on: ubuntu-latest
needs: [formatblack] # Keeps the bot from updating the image if its not tested/formatted

permissions:
contents: read
packages: write

strategy:
matrix:
package:
- admin_bot
- admin_interface

steps:
- name: Checkout repository
uses: actions/checkout@v2
Expand All @@ -88,7 +93,7 @@ jobs:
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Extract Version information for admin_bot
- name: Extract Version information for ${{ matrix.package }}
id: extract_hash
run: |
echo BUILD_HASH=$(git describe --abbrev=8 --always --tags --dirty) >> $GITHUB_ENV
Expand All @@ -101,12 +106,12 @@ jobs:
run: |
echo "${{ steps.branch-name.outputs.current_branch }}"
echo "${{ steps.meta.outputs.tags }}"
echo "CUSTOM_TAG=ghcr.io/frc-1721/adminbot:$(echo "${{ steps.branch-name.outputs.current_branch }}" | sed 's/[^a-zA-Z0-9]/-/g; s/[A-Z]/\L&/g')" >> $GITHUB_ENV
echo "CUSTOM_TAG=ghcr.io/frc-1721/$(echo ${{ matrix.package }} | sed 's/[A-Z_]\|L&//'):$(echo "${{ steps.branch-name.outputs.current_branch }}" | sed 's/[^a-zA-Z0-9]/-/g; s/[A-Z]/\L&/g')" >> $GITHUB_ENV
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
context: ${{ matrix.package }}/
push: true
build-args: |
GIT_COMMIT=${{ env.BUILD_HASH }}
Expand All @@ -119,4 +124,27 @@ jobs:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: "Built and published a new Adman Bot docker image to `${{ env.CUSTOM_TAG }}`!"
args: "Built and published a new `${{ matrix.package }}` docker image to `${{ env.CUSTOM_TAG }}`!"

gcr-cleaner:
runs-on: "ubuntu-latest"
needs: [build-and-publish-image]

steps:
- name: Clean adminbot
id: clean_adminbot
uses: actions/delete-package-versions@v4
with:
package-name: "adminbot"
package-type: "container"
min-versions-to-keep: 2
delete-only-untagged-versions: "true"

- name: Clean admininterface
id: clean_admininterface
uses: actions/delete-package-versions@v4
with:
package-name: "admininterface"
package-type: "container"
min-versions-to-keep: 2
delete-only-untagged-versions: "true"
97 changes: 32 additions & 65 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,75 +1,42 @@
.PHONY: help clean clean-pyc clean-build list test coverage release

# Help
help:
@echo " clean-build - Remove build artifacts"
@echo " clean-pyc - Remove Python file artifacts"
@echo " lint - Check style with flake8"
@echo " test - Run tests quickly with the default Python"
@echo " install-requirements - install the requirements for development"
@echo " build Builds the docker images for the docker-compose setup"
@echo " docker-rm Stops and removes all docker containers"
@echo " run Run a command. Can run scripts, e.g. make run COMMAND=\"./scripts/schema_generator.sh\""
@echo " shell Opens a Bash shell"
@echo " prod Is meant for running on the production env"

# Clean everything
clean: clean-build clean-pyc docker-rm

# Clean build data
clean-build:
rm -fr build/
rm -fr dist/
rm -fr *.egg-info

# Clean python cache
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +

# Delint the code
lint:
flake8 .

# Run the tests
test: build
docker-compose run admin_bot test

# Install requirements (locally)
install-requirements:
pip install -r requirements/requirements.txt
pip install -r requirements/test_requirements.txt

# Build container locally
build:
# Shortcut makefile list

help: ## Show this help.
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'


test: build ## Currently broken, requires build
docker compose run admin_bot test


configure-pipenv: ## sets up pipenv for you
pipenv install -r admin_bot/requirements/requirements.txt
pipenv install -r admin_interface/requirements/requirements.txt
pipenv shell


build: ## Build containers locally
# Generate tag
echo TAG=$(shell git rev-parse --abbrev-ref HEAD | sed 's/[^a-zA-Z0-9]/-/g') > .env

# Build
docker-compose build --build-arg GIT_COMMIT=$(shell git describe --abbrev=8 --always --tags --dirty) --build-arg DEBUG=True
docker compose build --build-arg GIT_COMMIT=$(shell git describe --abbrev=8 --always --tags --dirty) --build-arg DEBUG=True


docker-rm: stop ## Delete containers, requires stop
docker compose rm -f

# Delete container
docker-rm: stop
docker-compose rm -f

# Get container shell
shell:
docker-compose run --entrypoint "/bin/bash" admin_bot
shell: ## Get container shell
docker compose run --entrypoint "/bin/bash" admin_bot

# Run command in container
run: build
docker-compose run admin_bot $(COMMAND)

# Stop container
stop:
docker-compose down
docker-compose stop
run: build ## Run command in container, requires build
docker compose up

# "production"
prod:
docker-compose -f docker-compose-prod.yml up -d
stop: ## Stop containers
docker compose down
docker compose stop

# Development stuff
dev:
docker-compose -f docker-compose.yml build && docker-compose -f docker-compose.yml up
# Specific stuff
flask-local: ## Runs flask locally for you!
cd admin_interface/admin_interface && flask --app main.py run
File renamed without changes.
5 changes: 2 additions & 3 deletions Dockerfile → admin_bot/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# See here https://hub.docker.com/r/gorialis/discord.py/
FROM gorialis/discord.py
FROM python:3.11

# Authors
LABEL authors="[email protected]"
Expand All @@ -9,7 +8,7 @@ ARG APP_NAME=admin-bot
ENV APP_NAME=${APP_NAME}

# Install special deps
RUN apt update && apt install -y texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra
RUN apt update && apt install -y texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra python-dev-is-python3

# Upgrade pip
RUN pip install --upgrade pip
Expand Down
10 changes: 10 additions & 0 deletions admin_bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Adminbot

This is the discord bot component.

To get started developing here, you'll need a `BOT_TOKEN`, you can get this by
making your own discord bot here https://discord.com/developers/applications

It can be a bit tricky to setup a server and a bot for testing but its worth it.
Use `^sync` to manually sync any discord slash commands to your development server,
and submit a pr!
File renamed without changes.
File renamed without changes.
174 changes: 174 additions & 0 deletions admin_bot/admin_bot/cogs/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Tidal Force robotics
# 2021, Admin Bot
# MIT License


import os
import time
import json
import pytz
import string
import random
import discord
import logging
import asyncio
import requests
import subprocess

from typing import Literal, Optional
from discord import app_commands
from discord.ext import commands, tasks
from ics import Calendar

from PIL import Image, ImageDraw, ImageFilter, UnidentifiedImageError

from profanity_check import predict, predict_prob

from utilities.common import seconds_until


# Minimal cog to provide an interface to the digital signage


class InterfaceCog(commands.Cog, name="Interface"):
def __init__(self, bot):
self.bot = bot # Local instance of bot

@app_commands.command(name="clear_promo")
@commands.has_any_role("Leads", "Adult Mentor", "Student Mentor")
async def clearPromo(self, ctx: discord.Interaction):
"""
Removes all promo materials in rotation
"""
msg = "Removing: "

dir = "/app/promo"
for f in os.listdir(dir):
msg += f"{f}, "
os.remove(os.path.join(dir, f))

await ctx.response.send_message(msg)

@app_commands.command(name="show_promo")
@commands.has_any_role("Leads", "Adult Mentor", "Student Mentor")
async def showPromo(self, ctx: discord.Interaction):
"""
Lists all the currently shown promos
"""
msg = "Promo List: "

dir = "/app/promo"
for f in os.listdir(dir):
# if its a json load it
if ".json" in f:
with open(dir + "/" + f) as file:
rawData = json.loads(file.read())

# update expires
_days = (time.time() - int(rawData["expires"])) / 86400
rawData["expires"] = f"{_days:.2f} days"

msg += f"```{rawData}```"

await ctx.response.send_message(msg)

@app_commands.command(name="submit_promo")
@commands.has_any_role("Leads", "Adult Mentor", "Student Mentor")
async def submitPromo(
self, ctx: discord.Interaction, img: str, days: int, caption: str = None
):
"""
Submits a promotional image to be displayed in rotation.
"""

if days > 90:
await ctx.response.send_message(
"Sorry, thats too long for a submitted image to be in rotation. Contact <@&614313406345904148> to submit a permanent promo image."
)
else:
# This may take a moment
await ctx.response.defer()
try:
# Make a random string
_rand = "".join(
random.choices(string.ascii_lowercase + string.digits, k=7)
)
# Create a full filename
filename = f"/app/promo/{(ctx.user.nick).replace(' ', '_')}_{_rand}"

# Write metadata first
imgData = {
"expires": f"{int(time.time()) + (86400*days)}",
"author": f"Submitted by {ctx.user.nick}",
"caption": caption,
}

json_object = json.dumps(imgData, indent=4)
with open(f"{filename}.json", "w") as outfile:
outfile.write(json_object)

# Download the file
subprocess.run(["wget", img, "-O", f"{filename}.png"])

await ctx.followup.send(
f"Done! Added `{filename}`, expires in `{days}` days! Caption was `{caption}`.",
file=discord.File(f"{filename}.png"),
)
except Exception as e:
await ctx.followup.send(f"Sorry! There was an error! {e}")

@commands.Cog.listener()
async def on_message(self, ctx: discord.message.Message):
"""
Pushes messages into the postgresql database.
"""

# We dont want to publish EVERY channel, just some specific ones!
allowed_channels = [
634136760401526793, # announcements
# 590312336414212107, # mechanical-cad
# 1075174212723032064, # electrical
# 590312300695650305, # software
# 776835421976002570, # outreach-buisness
# 1024362276951703552, # media
# 1077252589697126523, # Quotes
719692563405078620, # Joe's testing channel
]

# Check if bot or in pm
if (
not isinstance(ctx.channel, discord.channel.DMChannel)
and not ctx.author.bot
):
logging.info(
f"Sending message from {ctx.channel.id}, author was {ctx.author.name}, forwarding it!"
)

webhookUrl = "http://interface:8000/dashboard/hook"

dataToSend = {
"channel": ctx.channel.name,
"author": ctx.author.display_name,
"author_avatar": str(ctx.author.avatar.url),
"content": ctx.content,
"version": self.bot.version,
}

if ctx.channel.id in allowed_channels: # If in allowed channel
if predict([ctx.content]) == 0: # If not profanity
req = requests.post( # post
url=webhookUrl,
data=json.dumps(dataToSend),
headers={"Content-type": "application/json"},
)
logging.debug(f"Sent webhook to signage, req was {req}")
await ctx.add_reaction("📧")
else:
logging.warn(f"Profanity detected in {ctx.content}.")
await ctx.add_reaction("⁉️")
else:
logging.debug(f"Channel {ctx.channel.id} was not in allowed channels.")


async def setup(bot):
await bot.add_cog(InterfaceCog(bot))
File renamed without changes.
Loading

0 comments on commit 178e959

Please sign in to comment.