Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99386b99e1 | ||
|
|
571c1c73e1 | ||
|
|
337d332638 | ||
|
|
78817a9193 | ||
|
|
ed600bae22 | ||
|
|
55fa4d7aa8 | ||
|
|
e74169291f | ||
|
|
66d28eb649 | ||
|
|
3f5a7f2c2a | ||
|
|
5ea4ecfe29 | ||
|
|
1b5858e491 | ||
|
|
9d091504ef | ||
|
|
8b28c50266 | ||
|
|
95e7002447 | ||
|
|
a92fc0f291 | ||
|
|
96090f8f8c | ||
|
|
8ef54a6306 | ||
|
|
9a139de37a | ||
|
|
bd671a4492 | ||
|
|
131a90a0e0 | ||
|
|
defa866a56 | ||
|
|
ba8a714bc2 | ||
|
|
d01b879003 | ||
|
|
26056e25a9 | ||
|
|
fc88f93fb3 | ||
|
|
5a467dee04 | ||
|
|
afc6e42570 | ||
|
|
28fc539601 |
@@ -1,24 +0,0 @@
|
||||
name: Docker Image CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Build the Docker image
|
||||
run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)
|
||||
- name: Docker Publish
|
||||
uses: manusa/actions-publish-docker@v1.0.1
|
||||
with:
|
||||
# Name of the Docker image
|
||||
name: m42e/bibreminder
|
||||
# Username for Docker registry
|
||||
username: ${{ secrets.Dockeruser }}
|
||||
# Password for Docker registry
|
||||
password: ${{ secrets.Dockertoken }}
|
||||
|
||||
+4
-12
@@ -1,18 +1,10 @@
|
||||
FROM python:3.8-alpine as base
|
||||
FROM python:3.7
|
||||
|
||||
FROM base as builder
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix=/install -r /requirements.txt
|
||||
RUN useradd --create-home appuser
|
||||
|
||||
COPY requirements.txt /home/appuser
|
||||
RUN pip install -r /home/appuser/requirements.txt
|
||||
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||
RUN apk add --no-cache tzdata
|
||||
ENV TZ=Europe/Berlin
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /home/appuser
|
||||
USER appuser
|
||||
|
||||
@@ -1,37 +1,14 @@
|
||||
# Bib Reminder for Munich public library
|
||||
|
||||
This automatically sending pushover messages when a lent item is about to be returned, while also extending the period of the lending if possible.
|
||||
|
||||
It will remind you 20 and 15 days before you have to return the media And if there are only 10 or less days left, it gets pretty annoying and bothers you each and every day, till you return it.
|
||||
|
||||
It's not much code nor is it complicated, but maybe it prevents someone else from paying for books kept to long.
|
||||
This automatically sending pushover messages when a lent item is about to be returned, while also extending the period if possible.
|
||||
|
||||
Configure the docker container with the following variables.
|
||||
|
||||
## Docker Config
|
||||
|
||||
The container by default keeps running. It sleeps till it is time to check again. But you can configure it to be a one shot.
|
||||
It does not keep any track of any data.
|
||||
|
||||
### Required
|
||||
- `PUSHOVER_KEY` Your pushover API Key
|
||||
- `PUSHOVER_CLIENTS` A comma seperated list of pushover client ids. All the notifications will be sent to this ids
|
||||
- `BIB_USERS` A comma seperated list of combinations of <USER>:<PWD> combinations to check and extend lent items.
|
||||
|
||||
|
||||
### Optional:
|
||||
- `LIBRARY_URL` The url of the OPAC entry page of your library, defaults to the one of Stadtbibliothek München
|
||||
- `RUN_AT_HOUR` Check at hour, default 6
|
||||
- `RUN_AT_MINUTE` Check at minute, default 0
|
||||
- `HEALTHCHECK_URL` OPTIONAL url for performing a healthcheck (see healthckeck.io for details)
|
||||
- `NOTIFY_USERS` A coma separated list of <USER>:<pushover client id> tulles to send pushover messages selectivly
|
||||
- `RUN_FOREVER` If set to `False` (mind the capitalization) the container will only perform a one time check. This way you could trigger it externally e.g. using cron.
|
||||
|
||||
### Example:
|
||||
|
||||
```
|
||||
PUSHOVER_KEY=abcdef1234567890abcdef12345678
|
||||
HEALTHCHECK_URL=https://healthcheck.io/ping/0000000-0000-0000-0000-000000000000
|
||||
BIB_USERS=400001234567:password,40000987654:drowssap
|
||||
NOTIFY_USERS=400001234567:12345678abcdef1234567890abcdef,40000987654:12345678abcdef1234567890abcdef
|
||||
```
|
||||
`PUSHOVER_KEY` Your pushover API Key
|
||||
`PUSHOVER_CLIENTS` A comma seperated list of pushover client ids.
|
||||
`HEALTHCHECK_URL` An url for performing a healthcheck (see healthckeck.io for details)
|
||||
`BIB_USERS` A comma seperated list of combinations of <USER>:<PWD> combinations to check and extend lent items.
|
||||
|
||||
+33
-110
@@ -8,138 +8,61 @@ import mechanize
|
||||
import pushover
|
||||
import datetime
|
||||
import os
|
||||
import logging
|
||||
|
||||
#logging.basicConfig(level=getattr(logging, os.environ.get("LOG_LEVEL", "INFO")))
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def split_notify(string):
|
||||
values = string.split(":")
|
||||
values = string.split(':')
|
||||
return (values[0], values[1:])
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
pushover.init(os.environ["PUSHOVER_KEY"])
|
||||
except:
|
||||
_logger.exception("Pushover KEY is required")
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
_logger.info("Starting Bibcheck")
|
||||
if "HEALTHCHECK_URL" in os.environ:
|
||||
_logger.info("Pinging Healthcheck [start]")
|
||||
_logger.debug(
|
||||
f"Pinging Healthcheck Url {os.environ['HEALTHCHECK_URL']}/start"
|
||||
)
|
||||
requests.get(f"{os.environ['HEALTHCHECK_URL']}/start")
|
||||
allinfo = []
|
||||
users = list(
|
||||
map(lambda x: x.split(":", 1), os.environ["BIB_USERS"].split(","))
|
||||
)
|
||||
notify = dict(map(split_notify, os.environ["NOTIFY_USERS"].split(",")))
|
||||
_logger.info(f"Found {len(users)} user(s) to check for")
|
||||
for user, pwd in users:
|
||||
_logger.info(f"Check for {user}")
|
||||
allinfo += check(user, pwd, notify.get(user, []))
|
||||
if "HEALTHCHECK_URL" in os.environ:
|
||||
_logger.info("Pinging Healthcheck [stop]")
|
||||
requests.post(
|
||||
f"{os.environ['HEALTHCHECK_URL']}",
|
||||
data="\n".join(allinfo).encode("utf8"),
|
||||
)
|
||||
if "RUN_FOREVER" in os.environ and os.environ["RUN_FOREVER"] == "False":
|
||||
_logger.info("Finished")
|
||||
break
|
||||
except Exception as e:
|
||||
_logger.exception("Check Failed ")
|
||||
if "HEALTHCHECK_URL" in os.environ:
|
||||
_logger.info("Pinging Healthcheck [failed]")
|
||||
_logger.debug(
|
||||
f"Pinging Healthcheck Url {os.environ['HEALTHCHECK_URL']}/fail"
|
||||
)
|
||||
requests.post(
|
||||
f"{os.environ['HEALTHCHECK_URL']}/fail",
|
||||
data="\n".join(allinfo).encode("utf8"),
|
||||
)
|
||||
pushover.init(os.environ['PUSHOVER_KEY'])
|
||||
if 'HEALTHCHECK_URL' in os.environ:
|
||||
requests.get(f"{os.environ['HEALTHCHECK_URL']}/start")
|
||||
allinfo = []
|
||||
users = list(map(lambda x: x.split(':', 1), os.environ['BIB_USERS'].split(',')))
|
||||
notify = dict(map(split_notify, os.environ['NOTIFY_USERS'].split(',')))
|
||||
for user, pwd in users:
|
||||
allinfo += check(user, pwd, notify.get(user, []))
|
||||
if 'HEALTHCHECK_URL' in os.environ:
|
||||
requests.post(f"{os.environ['HEALTHCHECK_URL']}", data='\n'.join(allinfo).encode('utf8'))
|
||||
if 'RUN_FOREVER' in os.environ and os.environ['RUN_FOREVER'] == 'False':
|
||||
break
|
||||
|
||||
now = datetime.datetime.utcnow
|
||||
to = (now() + datetime.timedelta(days=1)).replace(
|
||||
hour=os.environ.get("RUN_AT_HOUR", 6),
|
||||
minute=os.environ.get("RUN_AT_MINUTE", 0),
|
||||
second=0,
|
||||
)
|
||||
_logger.info(f"Sleeping till {to.isoformat()}")
|
||||
time.sleep((to - now()).seconds)
|
||||
|
||||
to = (now() + datetime.timedelta(days = 1)).replace(hour=6, minute=0, second=0)
|
||||
time.sleep((to-now()).seconds)
|
||||
|
||||
def check(username, password, notify_ids):
|
||||
br = mechanize.Browser()
|
||||
starturl = os.environ.get(
|
||||
"LIBRARY_URL",
|
||||
"https://ssl.muenchen.de/aDISWeb/app?service=direct/0/Home/$DirectLink&sp=SOPAC&sp=SBK00000000",
|
||||
)
|
||||
_logger.info(f"Library URL used: {starturl}")
|
||||
_logger.info(f"Goto loginpage")
|
||||
starturl = 'https://ssl.muenchen.de/aDISWeb/app?service=direct/0/Home/$DirectLink&sp=SOPAC'
|
||||
response = br.open(starturl)
|
||||
_logger.info(f"Fill form")
|
||||
br.select_form("Form0")
|
||||
br["$Textfield"] = username
|
||||
br["$Textfield$0"] = password
|
||||
_logger.info(f"Sumbit form")
|
||||
response = br.submit(name='textButton')
|
||||
print (b'Matthias' in response.read())
|
||||
currenturl = response.geturl()
|
||||
cookies = br.cookiejar
|
||||
# _logger.info(f"Open account page using form0")
|
||||
|
||||
#br.select_form(nr=0)
|
||||
#br.set_all_readonly(False)
|
||||
#br["$ScriptButton_hidden"] = 'BK'
|
||||
br.set_debug_http(True)
|
||||
# response = br.submit()
|
||||
br.follow_link(text_regex=r"Anmelden")
|
||||
br.select_form('Form0')
|
||||
br['$Textfield'] = username
|
||||
br['$Textfield$0'] = password
|
||||
response = br.submit()
|
||||
br.follow_link(text_regex=r"Konto")
|
||||
try:
|
||||
_logger.info(f"Open lent list")
|
||||
req = mechanize.Request(currenturl + '?service=direct/1/POOLSTPM@@@@@@@@_4400E200_3B328A80/Tabelle_Z1LP01.cellInternalLink.directlink&sp=SRGLINK_5&sp=SZA&requestCount=1',method='GET',headers={'Referer': currenturl})
|
||||
cookies.add_cookie_header(req)
|
||||
response = br.open(req)
|
||||
print(response.read())
|
||||
_logger.info(f"Extend all")
|
||||
br.select_form("Form0")
|
||||
response = br.submit(name="textButton$0", label="Alle verlängern")
|
||||
lentlist = bs4.BeautifulSoup(response.read(), "html.parser")
|
||||
response = br.follow_link(text_regex=r"Ausleihen? zeigen")
|
||||
br.select_form('Form0')
|
||||
response = br.submit(name='textButton$0', label='Alle verlängern')
|
||||
lentlist = bs4.BeautifulSoup(response.read(), 'html.parser')
|
||||
table = lentlist.select('table[class="rTable_table"]')[0]
|
||||
allinfo = []
|
||||
_logger.info(f"Parsing table")
|
||||
for entry in table.tbody.select("tr"):
|
||||
info = list(map(lambda x: str(x.text).strip(), entry.select("td")))
|
||||
date = datetime.datetime.strptime(info[1], "%d.%m.%Y")
|
||||
for entry in table.tbody.select('tr'):
|
||||
info = list(map(lambda x: str(x.text).strip(), entry.select('td')))
|
||||
date = datetime.datetime.strptime(info[1], '%d.%m.%Y')
|
||||
delta = date - datetime.datetime.now()
|
||||
allinfo.append(str(info))
|
||||
if delta.days <= 10 or delta.days == 20 or delta.days == 15:
|
||||
message = f"Bitte an {info[3]} denken\n"
|
||||
if delta.days <= 7:
|
||||
message += '<font color="#ff0000">'
|
||||
message += f"Abgabe <b>{info[1]}</b>"
|
||||
if delta.days <= 7:
|
||||
message += "</font>"
|
||||
message += f" - {username}"
|
||||
for client in itertools.chain(
|
||||
notify_ids, os.environ.get("PUSHOVER_CLIENTS", "").split(",")
|
||||
):
|
||||
try:
|
||||
pushover.Client(client).send_message(
|
||||
message, title="Erinnerung", html=1
|
||||
)
|
||||
except:
|
||||
print("No client")
|
||||
for client in itertools.join(notify_ids, os.environ['PUSHOVER_CLIENTS'].split(',')):
|
||||
pushover.Client(client).send_message(f'Bitte an {info[3]} denken, Abgabe {info[3]} - {username}', title="Erinnerung")
|
||||
except (StopIteration, mechanize._mechanize.LinkNotFoundError) as e:
|
||||
_logger.exception("Failed to read information")
|
||||
for client in itertools.join(notify_ids, os.environ['PUSHOVER_CLIENTS'].split(',')):
|
||||
pushover.Client(client).send_message(f'nichts ausgeliehen {username} ({e})')
|
||||
return []
|
||||
return allinfo
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user