refactored into python3 script for discourse api
This commit is contained in:
parent
0fecad10ad
commit
d204f74d82
7 changed files with 148 additions and 105 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
invite.conf
|
16
README.md
16
README.md
|
@ -1,26 +1,18 @@
|
||||||
# Einladung zum Plenum des Hackspace Jena e.V.
|
# Einladung zum Plenum des Hackspace Jena e.V.
|
||||||
|
|
||||||
Das Repository enthält Dateien zum automatischen Versenden von Einladungsmails
|
Das Repository enthält Dateien zum automatischen Versenden von Einladungsmails für das Plenum.
|
||||||
für das Plenum.
|
|
||||||
|
|
||||||
Das Shellskript ermittelt den kommenden Donnerstag, prüft, ob es ein erster
|
Die Service- und Timer-Dateien lassen systemd das Skript einmal monatlich ausführen.
|
||||||
Donnerstag des Monats ist, und bricht sonst ab, ersetzt im Einladungstext (Datei
|
|
||||||
`email_text`) Platzhalter mit dem Datum des kommenden Donnerstag und verschickt
|
|
||||||
den Text als E-Mail.
|
|
||||||
|
|
||||||
Die Service- und Timer-Dateien lassen systemd das Skript einmal wöchentlich
|
|
||||||
ausführen.
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Dieses Repository muss nach `/opt` in das Verzeichnis `plenumsinvite` kopiert
|
Dieses Repository soll nach `/opt` in das Verzeichnis `plenumsinvite` kopiert werden:
|
||||||
werden:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir -p /opt
|
mkdir -p /opt
|
||||||
cd /opt
|
cd /opt
|
||||||
git clone https://github.com/HackspaceJena/plenums_invite.git
|
git clone https://github.com/HackspaceJena/plenums_invite.git
|
||||||
cd /plenums_invite
|
cd plenums_invite
|
||||||
```
|
```
|
||||||
|
|
||||||
Danach müssen die beiden systemd-Unit-Dateien nach `/etc/systemd/system`
|
Danach müssen die beiden systemd-Unit-Dateien nach `/etc/systemd/system`
|
||||||
|
|
22
email_text
22
email_text
|
@ -1,22 +0,0 @@
|
||||||
Hallo zusammen,
|
|
||||||
|
|
||||||
am PROSEDATE findet wieder unser monatliches Plenum statt.
|
|
||||||
Wir treffen uns um 20 Uhr im virtuellen Raum unter:
|
|
||||||
|
|
||||||
https://talk.kabi.tk/krautspace
|
|
||||||
|
|
||||||
Für Vorbereitung und Protokoll gibt es wieder ein Pad, bitte
|
|
||||||
tragt schon mal Themen ein, die Ihr gern besprechen würdet:
|
|
||||||
|
|
||||||
https://vereinte.verwirrung.institute/p/ksplenum-URLDATE
|
|
||||||
|
|
||||||
Ggf. muss das Pad erst noch vorbereitet werden, die Vorlage
|
|
||||||
mit Anleitung findet Ihr hier:
|
|
||||||
|
|
||||||
https://vereinte.verwirrung.institute/p/ksplenum-template
|
|
||||||
|
|
||||||
Wir freuen uns auf zahlreiche Teilnehmer:innnen.
|
|
||||||
|
|
||||||
Mit den besten Grüßen,
|
|
||||||
|
|
||||||
das Einladeskript
|
|
22
invite.ics
22
invite.ics
|
@ -1,22 +0,0 @@
|
||||||
BEGIN:VCALENDAR
|
|
||||||
PRODID:-//KrautSpace Jena//KrautSpace Einladescript//DE
|
|
||||||
VERSION:2.0
|
|
||||||
CALSCALE:GREGORIAN
|
|
||||||
METHOD:PUBLISH
|
|
||||||
BEGIN:VEVENT
|
|
||||||
DTSTART:#DTSTART#
|
|
||||||
DTEND:#DTEND#
|
|
||||||
DTSTAMP:#DTSTAMP#
|
|
||||||
UID:#DTSTAMP#@kraut.space
|
|
||||||
SUMMARY:#SUMMARY#
|
|
||||||
DESCRIPTION:Wir treffen uns um 20 Uhr im virtuellen Raum unter:\n#LOCATION#\n\nFür Vorbereitung und Protokoll gibt es wieder ein Pad, bitte tragt schon mal Themen ein, die Ihr gern besprechen würdet:\nhttps://vereinte.verwirrung.institute/p/ksplenum-#URLDATE#\n\nGgf. muss das Pad erst noch vorbereitet werden, die Vorlage mit Anleitung findet Ihr hier:\nhttps://vereinte.verwirrung.institute/p/ksplenum-template
|
|
||||||
LOCATION:#LOCATION#
|
|
||||||
ORGANIZER;ROLE=CHAIR;PARTSTAT=ACCEPTED;RSVP=FALSE:mailto:#FROM#
|
|
||||||
CONFERENCE;VALUE=URI:#LOCATION#
|
|
||||||
SEQUENCE:0
|
|
||||||
STATUS:CONFIRMED
|
|
||||||
CLASS:PUBLIC
|
|
||||||
CATEGORIES:Meeting
|
|
||||||
TRANSP:OPAQUE
|
|
||||||
END:VEVENT
|
|
||||||
END:VCALENDAR
|
|
112
invite.py
Executable file
112
invite.py
Executable file
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import optparse
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class JsonObject(object):
|
||||||
|
'''An object constructed from a JSON response'''
|
||||||
|
|
||||||
|
def __init__(self, json):
|
||||||
|
self.update_attributes(json)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{} with ID: {}'.format(self.__class__.__name__, self.id)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
vars_string = str(self.__dict__)
|
||||||
|
|
||||||
|
replace = {': ': '=', '{': '', '}': ''}
|
||||||
|
for key in replace:
|
||||||
|
vars_string = vars_string.replace(key, replace[key])
|
||||||
|
|
||||||
|
return '{}({})'.format(self.__class__.__name__, vars_string)
|
||||||
|
|
||||||
|
def update_attributes(self, json):
|
||||||
|
self.__dict__.update(json)
|
||||||
|
|
||||||
|
class Topic(JsonObject):
|
||||||
|
|
||||||
|
def __init__(self, client, **kwargs):
|
||||||
|
self.client = client
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
|
||||||
|
def __init__(self, host, api_username='', api_key=''):
|
||||||
|
self.host = host
|
||||||
|
|
||||||
|
self.session = requests.Session()
|
||||||
|
self.session.headers.update({
|
||||||
|
'Api-Username': api_username,
|
||||||
|
'Api-Key': api_key,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Class Methods
|
||||||
|
def _request(self, method, path, params=None, data=None, json=None):
|
||||||
|
response = self.session.request(
|
||||||
|
method=method.upper(),
|
||||||
|
url=requests.compat.urljoin(self.host, path),
|
||||||
|
params=params,
|
||||||
|
data=data,
|
||||||
|
json=json,
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def create_topic(self, title, raw, category=None):
|
||||||
|
response = self._request('POST', 'posts.json', json={
|
||||||
|
'title': title,
|
||||||
|
'raw': raw,
|
||||||
|
'category': category,
|
||||||
|
})
|
||||||
|
|
||||||
|
return Topic(client=self, json=response)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
op = optparse.OptionParser()
|
||||||
|
op.add_option("-c", "--config", default="invite.conf")
|
||||||
|
op.add_option("-v", "--verbose", action="store_true")
|
||||||
|
options, args = op.parse_args()
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
configfile = options.config
|
||||||
|
if not config.read(configfile):
|
||||||
|
op.error(f"Configuration file '{configfile}' not found or not readable.")
|
||||||
|
|
||||||
|
if options.verbose:
|
||||||
|
logging.basicConfig()
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
client = Client(
|
||||||
|
host = config['discourse']['uri'],
|
||||||
|
api_username = config['discourse']['api_username'],
|
||||||
|
api_key = config['discourse']['api_key']
|
||||||
|
)
|
||||||
|
|
||||||
|
now = datetime.date.today()
|
||||||
|
date = now + datetime.timedelta(3 - now.weekday())
|
||||||
|
date_str = date.strftime('%d.%m.%Y')
|
||||||
|
|
||||||
|
subject = f'Einladung zum Plenum am {date_str}'
|
||||||
|
msg_body = '''Hallo zusammen,
|
||||||
|
|
||||||
|
am Donnerstag findet wieder unser monatliches Plenum statt.
|
||||||
|
Wir treffen uns um 20 Uhr im virtuellen Raum unter:
|
||||||
|
|
||||||
|
https://talk.kabi.tk/krautspace
|
||||||
|
|
||||||
|
Antwortet einfach hier auf diesen Post, wenn ihr etwas thematisieren möchtet.
|
||||||
|
Wir freuen uns auf zahlreiche Teilnehmer:innnen.
|
||||||
|
|
||||||
|
Mit den besten Grüßen,
|
||||||
|
|
||||||
|
das Einladeskript'''
|
||||||
|
client.create_topic(title=subject, raw=msg_body, category=7)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
47
invite.sh
47
invite.sh
|
@ -1,47 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
from="office@krautspace.de"
|
|
||||||
to=("hackspace-jena@lstsrv.org" "krautspace-announce@lstsrv.org")
|
|
||||||
#replyto="hackspace-jena@lstsrv.org"
|
|
||||||
subject="Einladung zum Plenum"
|
|
||||||
location="https://talk.kabi.tk/krautspace"
|
|
||||||
body_file=email_text # Use PROSEDATE for dd.mm.YYYY and URLDATE for YYYmmdd
|
|
||||||
|
|
||||||
## Find next Thursday
|
|
||||||
date_fmt () { date -u -d "$2 next Thu" "$1"; }
|
|
||||||
|
|
||||||
## Check if next Thursday is the first of a month
|
|
||||||
|
|
||||||
if (( $(date_fmt +%_d) > 7 )); then
|
|
||||||
echo "Invitation should be sent at most a week in advance."
|
|
||||||
echo "Exiting without sending an inviation..."
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Send invitation
|
|
||||||
|
|
||||||
prose_date=$(date_fmt +%d.%m.%Y)
|
|
||||||
url_date=$(date_fmt +%Y%m%d)
|
|
||||||
|
|
||||||
script_path=$(readlink -f "$0")
|
|
||||||
source_directory=$(dirname "$script_path")
|
|
||||||
|
|
||||||
sed -E \
|
|
||||||
-e "s/#DTSTART#/$(date_fmt +%Y%m%dT%H%M%SZ "TZ=\"Europe/Berlin\" 20:00")/" \
|
|
||||||
-e "s/#DTEND#/$(date_fmt +%Y%m%dT%H%M%SZ "TZ=\"Europe/Berlin\" 21:00")/" \
|
|
||||||
-e "s/#DTSTAMP#/$(date -u +%Y%m%dT%H%M%SZ)/" \
|
|
||||||
-e "s/#SUMMARY#/KrautSpace Plenum/" \
|
|
||||||
-e "s%#LOCATION#%${location}%" \
|
|
||||||
-e "s/#FROM#/${from}/g" \
|
|
||||||
-e "s/#URLDATE#/${url_date}/g" \
|
|
||||||
-e 's/(^[^ ].{73}|.{73})/\1\r\n /g' \
|
|
||||||
"$source_directory/invite.ics" > /tmp/invite.ics
|
|
||||||
|
|
||||||
sed -e "s/PROSEDATE/$prose_date/g" \
|
|
||||||
-e "s/URLDATE/$url_date/g" \
|
|
||||||
"$source_directory/$body_file" | EMAIL="$from" mutt -s "$subject" -a /tmp/invite.ics -- "${to[@]}"
|
|
||||||
ret=$?
|
|
||||||
rm -f /tmp/invite.ics
|
|
||||||
exit $ret
|
|
|
@ -1,8 +1,37 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Send invitation to Hackspace's Plenum
|
Description=Send invitation to Hackspace's Announce Discourse
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/opt/plenums_invite/invite.sh
|
ExecStart=/opt/plenums_invite/invite.py
|
||||||
|
|
||||||
|
WorkingDirectory=/opt/plenums_invite
|
||||||
|
|
||||||
|
UMask=077
|
||||||
|
#DynamicUser=yes
|
||||||
|
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateUsers=yes
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
|
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectClock=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectKernelLogs=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProcSubset=pid
|
||||||
|
ProtectHostname=yes
|
||||||
|
|
||||||
|
ReadOnlyDirectories=/
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
MemoryDenyWriteExecute=true
|
||||||
|
RestrictRealtime=true
|
||||||
|
RestrictNamespaces=true
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
LockPersonality=yes
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6
|
||||||
|
|
Loading…
Reference in a new issue