mirror of
https://github.com/Luzifer/twitch-bot-rules.git
synced 2024-12-22 19:31: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