mirror of
https://github.com/Luzifer/twitch-bot-rules.git
synced 2024-12-22 11:21:18 +00:00
Initial repo setup
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
04cf00d63d
commit
1b58f13dc1
9 changed files with 334 additions and 0 deletions
67
.github/workflows/publish-index.yml
vendored
Normal file
67
.github/workflows/publish-index.yml
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
|
||||
name: Deploy Rules Index
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
container:
|
||||
image: luzifer/archlinux
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Install required packages
|
||||
run: |
|
||||
pacman -Syy --noconfirm \
|
||||
git \
|
||||
python \
|
||||
python-pip \
|
||||
make \
|
||||
tar \
|
||||
yamllint \
|
||||
yq
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Lint rules files
|
||||
run: make rules_lint
|
||||
|
||||
- name: Build rules index
|
||||
run: make index
|
||||
|
||||
- name: Setup Pages
|
||||
if: github.ref_name == 'main'
|
||||
uses: actions/configure-pages@v2
|
||||
|
||||
- name: Upload artifact
|
||||
if: github.ref_name == 'main'
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
if: github.ref_name == 'main'
|
||||
uses: actions/deploy-pages@v1
|
||||
|
||||
...
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
_site
|
||||
.venv
|
18
Makefile
Normal file
18
Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
OUTDIR :=_site
|
||||
export RULE_BASE := https://github.com/Luzifer/twitch-bot-rules/raw/main/
|
||||
|
||||
default:
|
||||
|
||||
rules_lint:
|
||||
bash ci/lint.sh
|
||||
|
||||
index: .venv
|
||||
rm -rf $(OUTDIR)
|
||||
mkdir $(OUTDIR)
|
||||
./.venv/bin/python ci/indexer.py \
|
||||
$(OUTDIR)/index.html \
|
||||
rules
|
||||
|
||||
.venv:
|
||||
python -m venv .venv
|
||||
./.venv/bin/pip install -r ci/requirements.yml
|
68
ci/indexer.py
Normal file
68
ci/indexer.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import jinja2
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
FIELD_REGEX = r"^# @([^ ]+)\s+(.*)$"
|
||||
|
||||
|
||||
def get_doc_field(content, field, default=None):
|
||||
matches = re.finditer(FIELD_REGEX, content, re.MULTILINE)
|
||||
|
||||
fields = {}
|
||||
|
||||
for matchNum, match in enumerate(matches, start=1):
|
||||
fields[match.groups()[0]] = match.groups()[1]
|
||||
|
||||
return fields[field] if field in fields else default
|
||||
|
||||
|
||||
def get_rules_index(rules_dir):
|
||||
rules = []
|
||||
|
||||
for file in os.listdir(rules_dir):
|
||||
fullpath = '/'.join([rules_dir, file])
|
||||
with open(fullpath, 'r') as rulefile:
|
||||
content = rulefile.read()
|
||||
|
||||
rule = yaml.load(content, Loader=yaml.SafeLoader)
|
||||
|
||||
rules.append({
|
||||
"actions": [x["type"] for x in rule["actions"]],
|
||||
"author": get_doc_field(content, "author"),
|
||||
"min_bot_ver": get_doc_field(content, "minBotVersion", "v3.x"),
|
||||
"description": rule["description"],
|
||||
"file": fullpath,
|
||||
"shortened_id": rule["uuid"].split('-')[0],
|
||||
"version": get_doc_field(content, "version", "v0"),
|
||||
})
|
||||
|
||||
return sorted(rules, key=lambda x: x["description"])
|
||||
|
||||
|
||||
def main(args):
|
||||
outfile = args[1]
|
||||
rules_dir = args[2]
|
||||
|
||||
rules = get_rules_index(rules_dir)
|
||||
|
||||
render_index(outfile, rules)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def render_index(outfile, rules):
|
||||
with open('index.tpl.html', 'r') as template_source:
|
||||
env = jinja2.Environment()
|
||||
tpl = env.from_string(template_source.read())
|
||||
|
||||
with open(outfile, 'w') as output:
|
||||
output.write(tpl.render(
|
||||
rule_base=os.environ['RULE_BASE'] if 'RULE_BASE' in os.environ else '/',
|
||||
rules=rules,
|
||||
))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
44
ci/lint.sh
Normal file
44
ci/lint.sh
Normal file
|
@ -0,0 +1,44 @@
|
|||
set -euo pipefail
|
||||
|
||||
exit_code=0
|
||||
|
||||
function error() {
|
||||
log E "$@"
|
||||
exit_code=1
|
||||
}
|
||||
|
||||
function info() {
|
||||
log I "$@"
|
||||
}
|
||||
|
||||
function log() {
|
||||
local level=$1
|
||||
shift
|
||||
echo "[$(date +%H:%M:%S)][$level] $@" >&2
|
||||
}
|
||||
|
||||
required_tags=(
|
||||
author
|
||||
minBotVersion
|
||||
version
|
||||
)
|
||||
|
||||
for rule_file in rules/*.yml; do
|
||||
|
||||
info "Linting rules file ${rule_file}"
|
||||
|
||||
info "+++ Checking with YAMLlint..."
|
||||
yamllint -c ci/yamllint.yml ${rule_file}
|
||||
|
||||
info "+++ Checking required tags..."
|
||||
for tag in "${required_tags[@]}"; do
|
||||
grep -Eq "^# @${tag} .+$" ${rule_file} || error "Missing required tag: ${tag}"
|
||||
done
|
||||
|
||||
info "+++ Checking subscription URL..."
|
||||
exp_url="${RULE_BASE}${rule_file}"
|
||||
sub_url="$(yq -r '.subscribe_from' ${rule_file})"
|
||||
[[ $sub_url == $exp_url ]] || error "Wrong subscription URL: expected ${exp_url}"
|
||||
done
|
||||
|
||||
exit $exit_code
|
3
ci/requirements.yml
Normal file
3
ci/requirements.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
Jinja2==3.1.2
|
||||
MarkupSafe==2.1.1
|
||||
PyYAML==6.0
|
51
ci/yamllint.yml
Normal file
51
ci/yamllint.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
# See documentation for configuration / rules here:
|
||||
# https://yamllint.readthedocs.io/en/stable/configuration.html
|
||||
#
|
||||
# - Put this file in ~/.config/yamllint/config to apply the rules
|
||||
# - Install yamllint using `pip install yamllint`
|
||||
# - Execute the check using `yamllint <path to yaml> [<path to yaml>]`
|
||||
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
braces:
|
||||
level: warning
|
||||
min-spaces-inside: 1
|
||||
max-spaces-inside: 1
|
||||
min-spaces-inside-empty: 0
|
||||
max-spaces-inside-empty: 1
|
||||
brackets:
|
||||
level: warning
|
||||
colons:
|
||||
level: warning
|
||||
commas:
|
||||
level: warning
|
||||
document-end:
|
||||
level: warning
|
||||
present: true
|
||||
document-start:
|
||||
level: warning
|
||||
present: true
|
||||
empty-lines:
|
||||
level: warning
|
||||
hyphens:
|
||||
level: warning
|
||||
indentation:
|
||||
level: warning
|
||||
spaces: 2
|
||||
indent-sequences: true
|
||||
check-multi-line-strings: true
|
||||
key-duplicates:
|
||||
level: error
|
||||
line-length:
|
||||
level: warning
|
||||
allow-non-breakable-inline-mappings: true
|
||||
new-line-at-end-of-file:
|
||||
level: warning
|
||||
trailing-spaces:
|
||||
level: warning
|
||||
|
||||
...
|
||||
|
||||
# vim: set ft=yaml:
|
60
index.tpl.html
Normal file
60
index.tpl.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Twitch-Bot Rules Archive</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" integrity="sha256-KTPJY0ik6ufLv48oDKCYFYaptcCX75UrmWytfSjy+tA=" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.2.1/dist/darkly/bootstrap.min.css" integrity="sha256-7mvDKMLoBU3B5Sj1sW2i33B9hPz1VSE9whs1QDJj01I=" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row mt-3">
|
||||
<div class="col">
|
||||
|
||||
<table class="table table-hover table-striped">
|
||||
<tr>
|
||||
<td>Rule</td>
|
||||
<td>Description</td>
|
||||
<td>Req. Bot<br>Version</td>
|
||||
</tr>
|
||||
{% for rule in rules %}
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
data-bs-title="Click to Copy Subscribe-URL"
|
||||
data-bs-toggle="tooltip"
|
||||
href="{{ rule_base }}{{ rule.file }}"
|
||||
onclick="navigator.clipboard.writeText('{{ rule_base }}{{ rule.file }}'); return false;"
|
||||
>
|
||||
{{ rule.shortened_id }}…
|
||||
</a>
|
||||
<p class="text-muted">
|
||||
<small><b>by</b> {{ rule.author }}</small><br>
|
||||
<small><b>ver</b> {{ rule.version}}</small>
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ rule.description }}</p>
|
||||
<p>
|
||||
{% for action in rule.actions %}
|
||||
<span class="badge bg-secondary">{{ action }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</td>
|
||||
<td>{{ rule.min_bot_ver }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha256-qFsv4wd3fI60fwah7sOZ/L3f6D0lL9IC0+E1gFH88n0=" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
window.addEventListener('load', () => {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
|
||||
# @author Luzifer
|
||||
# @minBotVersion v3.0
|
||||
# @version v1
|
||||
|
||||
uuid: a136bb59-02f9-4a2a-88a2-be719655a7e2
|
||||
subscribe_from: https://github.com/Luzifer/twitch-bot-rules/raw/main/rules/a136bb59-02f9-4a2a-88a2-be719655a7e2_wanna-become-famous.yml
|
||||
description: 'Spam: "Wanna become famous? Buy followers, primes and viewers"'
|
||||
|
||||
actions:
|
||||
- type: delete
|
||||
- type: ban
|
||||
attributes:
|
||||
reason: Chat-Spam "{{ group 1 }}"
|
||||
|
||||
match_message: (?i)(.*(?:Buy|Best)?(?:\s+(?:followers|primes|viewers)\s*(?:,|and|)){2,}\s+on.*)
|
||||
|
||||
disable_on_template: '{{ not (botHasBadge "moderator") }}'
|
||||
|
||||
...
|
Loading…
Reference in a new issue