├── .github └── ISSUE_TEMPLATE │ ├── fehlerhafte-datenlisten.md │ └── neue-datenquelle.md ├── .gitignore ├── .gitmodules ├── .pylintrc ├── AnswerMachine ├── __init__.py ├── candidate.py ├── handle_list.py ├── react.py └── result.py ├── Documentation ├── __init__.py ├── dump.py ├── dump_il.py ├── generator.py ├── licenses.py └── markdowndoc.py ├── Externals ├── Measure.py ├── __init__.py ├── database.py ├── mastodon.py ├── message.py ├── network.py └── user.py ├── Import ├── __init__.py ├── access.py ├── datasource.py ├── error.py ├── row.py └── sourceconfig.py ├── LICENSE ├── Persistence ├── __init__.py ├── gitdescribe.py └── since.py ├── README.md ├── cards ├── socialcard.png └── socialcard.svg ├── config ├── api_weights.json └── schema.sql ├── data ├── README.md ├── bot.json ├── faq.json ├── linien_bhvbus.json ├── linien_bsag.json ├── linien_by.json ├── linien_dd.json ├── linien_de_fv.json ├── linien_delbus.json ├── linien_ffm.json ├── linien_hg.json ├── linien_koeln.json ├── linien_mv.json ├── linien_ni.json ├── linien_nl.json ├── linien_no.json ├── linien_nvs.json ├── linien_nw.json ├── linien_opr.json ├── linien_p.json ├── linien_rmv.json ├── linien_rnv.json ├── linien_rvf.json ├── linien_sh.json ├── linien_sn.json ├── linien_th.json ├── linien_vab.json ├── linien_vbb.json ├── linien_vvs.json ├── linien_wtv.json ├── orte_at.json ├── orte_be.json ├── orte_ca.json ├── orte_ch.json ├── orte_dd.json ├── orte_de.json ├── orte_dk.json ├── orte_ffm.json ├── orte_fr.json ├── orte_hh.json ├── orte_ibnr.json ├── orte_leitpunkte.json ├── orte_nl.json ├── orte_no.json ├── orte_nvs.json ├── orte_se.json ├── orte_uk.json ├── orte_wien.json ├── regeln_de.json ├── regeln_vkms.json ├── signale_bostrab.json ├── signale_de_ds301.json ├── signale_de_dv301.json ├── signale_de_gemein.json ├── signale_no.json ├── strecken_ch.json ├── strecken_de.json ├── strecken_ffm.json └── strecken_no.json ├── doc ├── aufbau-antworten.md ├── avatar-bahn.social.svg ├── avatar-ril100.svg ├── avatar-zug.network.svg ├── avatar.svg ├── bot.css ├── contribute.md ├── copyright.md ├── datenschutz.md ├── faq.md ├── finde-lang.md ├── finde-listen.md ├── finde-toots.md ├── haftung.md ├── ignorelist.md ├── impressum.md ├── index.md ├── interaktion.md ├── leerzeichen_ds100.md ├── links.snip ├── mastodon-icon.png ├── motivation.md ├── script.js └── twitter-icon.png ├── ds100bot ├── get_status ├── sources ├── README.md ├── bot.csv ├── faq.csv ├── linien_bhvbus.csv ├── linien_bsag.csv ├── linien_by.csv ├── linien_dd.csv ├── linien_de_fv.csv ├── linien_delbus.csv ├── linien_ffm.csv ├── linien_hg.csv ├── linien_koeln.csv ├── linien_mv.csv ├── linien_ni.csv ├── linien_nl.csv ├── linien_no.csv ├── linien_nvs.csv ├── linien_nw.csv ├── linien_nwm.csv ├── linien_opr.csv ├── linien_p.csv ├── linien_rmv.csv ├── linien_rnv.csv ├── linien_rvf.csv ├── linien_sh.csv ├── linien_sn.csv ├── linien_th.csv ├── linien_vab_kreis_ab.csv ├── linien_vab_kreis_mb.csv ├── linien_vab_stadt_ab.csv ├── linien_vbb.csv ├── linien_vlp.csv ├── linien_vvs.csv ├── linien_wtv.csv ├── orte_at_db640.csv ├── orte_be.csv ├── orte_ca_via.csv ├── orte_ch_add.csv ├── orte_ch_didok.csv ├── orte_dd.csv ├── orte_de_add.csv ├── orte_de_ds100.csv ├── orte_de_stationsdaten.csv ├── orte_dk.csv ├── orte_ffm.csv ├── orte_hamburg.csv ├── orte_leitpunkte.csv ├── orte_nl.csv ├── orte_no.csv ├── orte_nvs.csv ├── orte_se.csv ├── orte_sncf.csv ├── orte_uk_crs.csv ├── orte_uk_long.csv ├── orte_wien.csv ├── regeln_de.csv ├── regeln_vkms.csv ├── signale_bostrab.csv ├── signale_de.csv ├── signale_de_gemein.csv ├── signale_no.csv ├── strecken_ch_sbb.csv ├── strecken_de_alt.csv ├── strecken_de_kbs.csv ├── strecken_de_namen.csv ├── strecken_de_vzg.csv ├── strecken_ffm.csv └── strecken_no.csv ├── statistics ├── test ├── tests ├── test_find_tokens.py ├── test_network.py ├── test_process_commands.py └── test_process_magic.py └── tools ├── add_score_to_commit_msg ├── check ├── find_all_pys ├── manage-blacklist ├── parentdir.py └── setup /.github/ISSUE_TEMPLATE/fehlerhafte-datenlisten.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Fehlerhafte Datenlisten 3 | about: Issue für Datenfehler 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Um welche Datenliste handelt es sich:** 11 | Magic Hashtag + Typ, Explizite Quelle oder Name des Dumps (z.B., #DS100, #DS:, orte_de 12 | 13 | **Fehlt ein Datensatz?** 14 | Abkürzung: 15 | Erwarteter Langtext: 16 | 17 | **Ist ein Datensatz falsch?** 18 | Abkürzung: 19 | Aktueller Langtext laut Bot: 20 | Korrekter Langtext: 21 | 22 | (falls möglich): **URL des Tweet, der falsch beantwortet wurde:** 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/neue-datenquelle.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Neue Datenquelle 3 | about: Vorschläge für neue Datenquellen 4 | title: '' 5 | labels: daten:neu 6 | assignees: '' 7 | 8 | --- 9 | 10 | Vorschlag für neue Datenquelle: 11 | * Art der Quelle (#Orte, $Strecken, %Signale, &Regeln_Sonstiges, /Linien, Typen): […] 12 | * Kurzbeschreibung/Netz (z.B. „Niederländische Signalordnung“, „Japanische Linien“): […] 13 | * Link: […] 14 | * Ich möchte selbst daran arbeiten (Ja/Nein)*: […] 15 | * Die Daten stehen unter/stelle ich unter folgende Lizenz**: […] 16 | * Vorgeschlagenes Kürzel (z.B. 'CH') und Magic Hashtag (z.B. '\_CH'): […]/[…] 17 | 18 | ## Hinweise für Liniennetze: 19 | - Daten sollten nach Liniennetzen sortiert sein – also etwa nach Verkehrsverbünden oder S-Bahn-Netzen. 20 | - Soll ein Datensatz in verschiedenen Liniennetzen auftauchen, sollte der Text dafür identisch sein. 21 | - Der Langtext sollte eine sinnvolle Identifizierung des Linienweges zulassen: Unterwegshaltepunkte sind erlaubt, sollten aber nicht überhand nehmen. Siehe unten zur Länge der Texte. 22 | - Hat eine Linie unterschiedliche Laufwege, sollten diese sinnvoll dargestellt werden. 23 | - Keine zwei Einträge für Hin- und Rückrichtung. 24 | 25 | ## Hinweise für die Bearbeitung: 26 | Bitte die Dokumentation für [Konfigurationen](https://ds100.frankfurtium.de/data.html) und [Datenlisten](https://ds100.frankfurtium.de/sources.html) beachten. 27 | 28 | Ich habe die Regeln verstanden und bin damit einverstanden: […] 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | credentials.py 2 | version.py 3 | __pycache__ 4 | info.db 5 | *.html 6 | doc/output 7 | doc/dumps.snip 8 | tweet_details.py 9 | saved_tweets/ 10 | sonstiges/ 11 | 12 | # Python Virt-Env 13 | env/ 14 | 15 | # VSCode/VSCodium 16 | .vscode/ 17 | 18 | # IntelliJ 19 | .idea/ 20 | *.iml 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "GitVersion"] 2 | path = GitVersion 3 | url = git@github.com:baeuchle/gitversion.git 4 | -------------------------------------------------------------------------------- /AnswerMachine/__init__.py: -------------------------------------------------------------------------------- 1 | """Everything that concerns creating the bot's answers. 2 | 3 | Entrypoint is handle_list::handle_list""" 4 | 5 | from .handle_list import handle_list 6 | -------------------------------------------------------------------------------- /AnswerMachine/candidate.py: -------------------------------------------------------------------------------- 1 | """Abstracts an abbreviation candidate""" 2 | 3 | class Candidate: 4 | def __init__(self, parts, magic): 5 | if parts[0] is None or parts[0] == '': 6 | self.type_character = '#' 7 | else: 8 | self.type_character = parts[0] 9 | self.explicit_source = parts[1] 10 | self.abbr = parts[2] 11 | self.abbr = self.abbr[0] + self.abbr[1:].replace('_', ' ') 12 | self.abbr = ' '.join(self.abbr.split()) 13 | self.magic_hashtag = magic 14 | 15 | def get_dict(self): 16 | return { 17 | 'type_character': self.type_character, 18 | 'explicit_source': self.explicit_source, 19 | 'abbr': self.abbr, 20 | 'magic_hashtag': self.magic_hashtag 21 | } 22 | 23 | def __str__(self): 24 | return str(self.get_dict()) 25 | 26 | def __eq__(self, rhs): 27 | return self.__dict__ == rhs.__dict__ 28 | -------------------------------------------------------------------------------- /AnswerMachine/handle_list.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0114 2 | import logging 3 | from .react import process_commands, process_message 4 | logger = logging.getLogger('bot.' + __name__) 5 | 6 | def handle_list(network, database, magic_tags, magic_emojis): 7 | message_dict = network.all_relevant_status(magic_tags) 8 | for mid, message in message_dict.items(): 9 | # exclude some message: 10 | if message.is_not_eligible: 11 | logger.debug("Status %s is not eligible", mid) 12 | continue 13 | logger.info("Looking at status %d", mid) 14 | # handle #folgenbitte and #entfolgen and possibly other meta commands, but 15 | # only for explicit mentions. 16 | if message.is_explicit_mention: 17 | logger.info("Message explicitly mentions me") 18 | process_commands(message, network) 19 | if message.has_hashtag(magic_tags): 20 | logger.info("Message has magic hashtag") 21 | if message.has_hashtag(magic_emojis): 22 | logger.info("Message has magic emoji") 23 | # Process this message 24 | mode = message.get_mode(magic_tags, magic_emojis) 25 | dmt = message.default_magic_hashtag([*magic_tags, *magic_emojis]) 26 | process_message(message, 27 | network, 28 | database, 29 | magic_tags, 30 | magic_emojis, 31 | modus=mode, 32 | default_magic_tag=dmt) 33 | for other in message.get_other_posts( 34 | message_dict, 35 | mode=mode, 36 | network=network, 37 | database=database, 38 | magic_tags=magic_tags, 39 | magic_emojis=magic_emojis 40 | ): 41 | logger.debug("Processing message %d mode %s def magic hash tag %s", other.id, mode, dmt) 42 | process_message(other, 43 | network, 44 | database, 45 | magic_tags, 46 | magic_emojis, 47 | modus=mode, 48 | default_magic_tag=dmt) 49 | -------------------------------------------------------------------------------- /AnswerMachine/result.py: -------------------------------------------------------------------------------- 1 | """Abstracts the abbreviation search result""" 2 | 3 | import logging 4 | logger = logging.getLogger('bot.' + __name__) 5 | 6 | class Result: 7 | # pylint: disable=R0902 8 | def __init__(self, candidate, db): 9 | self.candidate = candidate 10 | row = self.find_in_db(db.cursor) 11 | self.abbr = candidate.abbr 12 | if row is not None: 13 | self.long = row['name'] 14 | self.status = row['status'] 15 | self.source = row['req_xs'] 16 | self.type = row['req_tc'] 17 | self.default_source = row['def_xs'] 18 | self.default_type = row['def_tc'] 19 | else: 20 | self.long = '' 21 | self.status = 'notfound' 22 | self.source = '' 23 | self.type = candidate.type_character 24 | self.default_source = '' 25 | self.default_type = '' 26 | logger.debug("%s → %s", candidate, self) 27 | 28 | def __str__(self): 29 | if self.status == 'notfound': 30 | return 'None' 31 | return self.normalized() + '::' + self.long 32 | 33 | def normalized(self): 34 | if self.status == 'notfound': 35 | return None 36 | return '{}{}:{}'.format(self.type, self.default_source, self.abbr) 37 | 38 | def answered(self): 39 | text = '{}: {}\u200b\n'.format(self.abbr, self.long) 40 | if not (self.default_source == 'DS' or self.source == 'BOT'): 41 | text = '{}{}{}'.format(self.source, self.type, text) 42 | return text.replace('\\n', '\n') 43 | 44 | def find_in_db(self, sql): 45 | sql.execute(""" 46 | SELECT 47 | shortstore.Abk as abk, 48 | shortstore.Name AS name, 49 | s1.explicit_source AS req_xs, 50 | s1.type AS req_tc, 51 | s2.explicit_source AS def_xs, 52 | s2.type AS def_tc, 53 | CASE 54 | WHEN ( 55 | blacklist.Abk IS NOT NULL 56 | AND 57 | :explicit_source = '' 58 | ) THEN 'blacklist' 59 | ELSE 'found' 60 | END 61 | AS status 62 | FROM 63 | shortstore 64 | JOIN sources AS s1 ON s1.source_id = shortstore.source 65 | JOIN sources AS s2 ON s1.source_id = s2.source_id 66 | JOIN magic_hashtags ON magic_hashtags.source_id = shortstore.source 67 | LEFT OUTER JOIN blacklist ON blacklist.Abk = shortstore.Abk AND s1.type = '#' 68 | WHERE 69 | shortstore.Abk = :abbr 70 | AND s1.type = :type_character 71 | AND ( 72 | (:explicit_source != '' AND s1.explicit_source = :explicit_source) 73 | OR 74 | (:explicit_source == '' 75 | AND (magic_hashtags.magic_hashtag = :magic_hashtag 76 | OR s1.explicit_source = 'BOT' 77 | ) 78 | )) 79 | AND s2.is_default 80 | """, 81 | self.candidate.get_dict() 82 | ) 83 | result = sql.fetchall() 84 | if not result: 85 | return None 86 | if len(result) == 1: 87 | return result[0] 88 | # if more than one: BOT has *no* precedence. 89 | for row in result: 90 | if row['req_xs'] != 'BOT': 91 | return row 92 | return result[0] 93 | 94 | def loggable(self, magic_tags): 95 | if self.status in ('found', 'blacklist'): 96 | return True 97 | if self.candidate.explicit_source != '': 98 | return True 99 | if len(self.abbr) == 0 or len(self.abbr) > 5: 100 | return False 101 | if '#' + self.abbr in magic_tags: 102 | return False 103 | return True 104 | -------------------------------------------------------------------------------- /Documentation/__init__.py: -------------------------------------------------------------------------------- 1 | """Collects everything for the successful creation of HTML Documentation""" 2 | 3 | from xml.etree import ElementTree as ET 4 | from pathlib import Path 5 | from .generator import Generator 6 | from .dump import Dump 7 | from .dump_il import DumpIgnorelist 8 | from .markdowndoc import MarkdownDoc 9 | from .licenses import Licenses 10 | 11 | def create_documentation(navi_list, md_dir, html_dir): 12 | for mdfile in md_dir.glob('*.md'): 13 | # don't consider copyright.md: 14 | if mdfile.stem == 'copyright': 15 | continue 16 | dp = MarkdownDoc(mdfile, navi_list) 17 | dp.write(html_dir / (mdfile.stem + '.html')) 18 | 19 | def dumplink_list(config_list): 20 | div = ET.Element('div', attrib={'class': 'dumpnavi hiddennavi'}) 21 | ET.SubElement(div, 'div', attrib={'class': 'closenavi'}, 22 | onclick="toggle_any('dumpnavi')").text = "Alle Dumps" 23 | linklist = ET.SubElement(div, 'ul') 24 | sublists = {} 25 | sigils = {'#': 'Orte', 26 | '$': 'Strecken', 27 | '/': 'Linien', 28 | '%': 'Signale', 29 | '&': 'Regeln' 30 | } 31 | for sigil, sigilname in sigils.items(): 32 | sublists[sigil] = ET.Element('ul', attrib={'class': f'dump_{sigilname.lower()}'}) 33 | for cid, conf in sorted(config_list.items()): 34 | item = ET.Element('li') 35 | ET.SubElement(item, 'a', attrib={'href': '/dumps/' + cid + '.html'}).text = conf.head 36 | if len(conf.file.stem) > 5: 37 | inserted_once = False 38 | for sigil in set(x.type for x in conf.access): 39 | if sigil in sublists: 40 | sublists[sigil].append(item) 41 | inserted_once = True 42 | if inserted_once: 43 | continue 44 | # Fallthrough from the above: 5 letters or less or unknown sigil. 45 | linklist.append(item) 46 | for sigil, subl in sublists.items(): 47 | classname = f'dump_{sigils[sigil].lower()}' 48 | sublistitem = ET.SubElement(linklist, 'li', attrib={'class': f'{classname} hiddennavi'}) 49 | listhl = ET.SubElement(sublistitem, 'div', onclick=f"toggle_any('{classname}')") 50 | ET.SubElement(listhl, 'span', attrib={'class': 'sigil'}).text = sigil 51 | ET.SubElement(listhl, 'span').text = sigils[sigil] 52 | sublistitem.append(subl) 53 | item = ET.SubElement(linklist, 'li') 54 | ET.SubElement(item, 'a', attrib={'href': '/dumps/ignorelist.html'}).text = 'Blacklist' 55 | return div 56 | 57 | def navilink_list(version): 58 | linkroot = ET.parse('doc/links.snip').getroot() 59 | for vtag in linkroot.findall(".//li[@class='transform_version']"): 60 | vtag.text = "Version: {}".format(version) 61 | return linkroot 62 | 63 | def create_dump_mainpage(navi, dump, dumpdir): 64 | mp = Generator('DS100-Daten-Dump Liste', links=navi) 65 | mp.headline('Liste der Datenquellen') 66 | dl = ET.SubElement(mp.main, 'navi') 67 | # We will manipulate dump, so we have to copy it first. 68 | # This is the only way I can find. 69 | dl.append(ET.fromstring(ET.tostring(dump))) 70 | dl.find('./div').set('class', 'bigdumplist') 71 | mp.write(dumpdir / 'index.html') 72 | 73 | def dump_source(config, dumplinks, navilinks, db, dumpdir): 74 | dump = Dump('DS100-Daten-Dump', config, dumplinks=dumplinks, links=navilinks) 75 | for entry_row in db.dumplist(config.id): 76 | dump.add_row(entry_row) 77 | dump.write(dumpdir / (config.id + '.html')) 78 | 79 | def dump_ignorelist(dumplinks, navilinks, db, dumpdir): 80 | dump = DumpIgnorelist(dumplinks=dumplinks, links=navilinks) 81 | for entry_row in db.dumpblack(): 82 | dump.add_row(entry_row) 83 | dump.write(dumpdir / 'ignorelist.html') 84 | -------------------------------------------------------------------------------- /Documentation/dump_il.py: -------------------------------------------------------------------------------- 1 | """Creates the dump of the Ignorelist""" 2 | 3 | from xml.etree import ElementTree as ET 4 | from .dump import Dump 5 | from .generator import Generator 6 | 7 | class DumpIgnorelist(Dump): 8 | def __init__(self, **kwargs): 9 | # pylint: disable=W0233 10 | Generator.__init__(self, 11 | 'DS100-Daten-Dump Ignorelist', 12 | desc='Alle Kürzel, die wegen anderer Bedeutungen normalerweise ignoriert werden', 13 | **kwargs) 14 | self.headline("Ignorelist") 15 | ET.SubElement(self.head, 'p').text = """Die folgenden Einträge werden nicht ohne explizite 16 | Quellenangabe (z.B. "#DS:") beantwortet.""" 17 | self.head.append(kwargs.get('dumplinks', None)) 18 | self.table = ET.SubElement(self.main, 'table', attrib={'class': 'dumptable'}) 19 | links = kwargs.get('links', None) 20 | if links is not None: 21 | self.head.append(links) 22 | self.number_of_data_lists = 2 23 | -------------------------------------------------------------------------------- /Documentation/generator.py: -------------------------------------------------------------------------------- 1 | """Base class for all HTML documentation generators""" 2 | 3 | from xml.etree import ElementTree as ET 4 | 5 | class Generator: 6 | SITENAME = {'property': 'og:site_name', 'content': 'DS100 Bot'} 7 | CARD = {'content': 'https://ds100.frankfurtium.de/socialcard.png'} 8 | LOCALE = {'property': 'og:locale', 'content': 'de_DE'} 9 | CARDTYPE = {'name': 'twitter:card', 'content': 'summary_large_image'} 10 | SITE = {'name': 'twitter:site', 'content': '@_ds_100'} 11 | CREATOR = {'name': 'twitter:creator', 'content': '@baeuchle'} 12 | def __init__(self, titletext, **kwargs): 13 | self.html = ET.Element('html', attrib={'prefix': 'og: https://ogp.me/ns#'}) 14 | head = ET.SubElement(self.html, 'head') 15 | head.append(ET.Element('meta', attrib={'charset': 'utf-8'})) 16 | head.append(ET.Element('meta', attrib={ 17 | 'name': 'viewport', 18 | 'content': 'width=device-width, initial-scale=1.0' 19 | })) 20 | head.append(ET.Element('link', attrib={ 21 | 'rel': 'stylesheet', 22 | 'type': 'text/css', 23 | 'href': '/bot.css' 24 | })) 25 | head.append(ET.Element('link', attrib={ 26 | 'rel': 'shortcut icon', 27 | 'type': 'image/svg+xml', 28 | 'href': 'https://avatar.frankfurtium.de/ds100.svg' 29 | })) 30 | ET.SubElement(head, 'script', attrib={ 31 | 'type': 'text/javascript', 32 | 'src': '/script.js' 33 | }).text = " " 34 | desc = {'content': kwargs.get('desc', titletext)} 35 | head.append(ET.Element('meta', attrib={'property': 'og:title', 'content': titletext})) 36 | head.append(ET.Element('meta', attrib=Generator.SITENAME)) 37 | head.append(ET.Element('meta', attrib={'property': 'og:image', **Generator.CARD})) 38 | head.append(ET.Element('meta', attrib={'property': 'og:description', **desc})) 39 | head.append(ET.Element('meta', attrib=Generator.LOCALE)) 40 | head.append(ET.Element('meta', attrib=Generator.CARDTYPE)) 41 | head.append(ET.Element('meta', attrib={'name': 'twitter:title', 'content': titletext})) 42 | head.append(ET.Element('meta', attrib={'name': 'twitter:description', **desc})) 43 | head.append(ET.Element('meta', attrib=Generator.SITE)) 44 | head.append(ET.Element('meta', attrib=Generator.CREATOR)) 45 | head.append(ET.Element('meta', attrib={'name': 'twitter:image', **Generator.CARD})) 46 | head.append(ET.Element('meta', attrib={'name': 'description', **desc})) 47 | title = ET.SubElement(head, 'title') 48 | title.text = titletext 49 | body = ET.SubElement(self.html, 'body') 50 | self.head = ET.SubElement(body, 'header') 51 | self.main = ET.SubElement(body, 'main') 52 | self.foot = ET.SubElement(body, 'footer') 53 | self.navi = ET.SubElement(self.foot, 'navi') 54 | ET.SubElement(self.navi, 'p', onclick="toggle_menu()").text = 'Menü' 55 | if 'links' in kwargs: 56 | self.navi.append(kwargs['links']) 57 | 58 | def headline(self, titletext): 59 | ET.SubElement(self.head, 'h1').text = titletext 60 | self.navi_in_head() 61 | 62 | def navi_in_head(self): 63 | navi = ET.SubElement(self.head, 'navi') 64 | ET.SubElement(navi, 'p', onclick="toggle_menu()").text = 'Menü' 65 | 66 | def write(self, targetfile): 67 | with targetfile.open(mode='w') as tf: 68 | ET.ElementTree(self.html).write(tf, 69 | encoding='unicode', method='xml') 70 | 71 | @classmethod 72 | def xmltext(cls, text): 73 | return ET.fromstring('' + text + '') 74 | -------------------------------------------------------------------------------- /Documentation/licenses.py: -------------------------------------------------------------------------------- 1 | """Generates the copyright file with license information""" 2 | 3 | from xml.etree import ElementTree as ET 4 | from .markdowndoc import MarkdownDoc 5 | from .generator import Generator 6 | 7 | class Licenses(MarkdownDoc): 8 | def __init__(self, mdfile, navi): 9 | super().__init__(mdfile, navi) 10 | self.add_table('Nach id sortiert') 11 | 12 | def add_table(self, headline): 13 | lic_table = ET.SubElement(self.main, 'table') 14 | if headline: 15 | ET.SubElement(lic_table, 'caption').text = headline 16 | thead = ET.SubElement(lic_table, 'thead') 17 | tr = ET.SubElement(thead, 'tr') 18 | for text in ('', 'Beschreibung', 19 | 'Dump', 'Magic Hashtag / Magic emoji'): 20 | th = ET.SubElement(tr, 'th') 21 | th.text = text 22 | self.lic = ET.SubElement(lic_table, 'tbody') 23 | 24 | def add_source(self, source): 25 | tr = ET.SubElement(self.lic, 'tr') 26 | ET.SubElement(tr, 'th').text = ', '.join( 27 | set('{}{}:'.format(a.type, a.explicit_source) for a in source.access) 28 | ) 29 | if source.desc: 30 | ET.SubElement(tr, 'td').append(Generator.xmltext(source.desc)) 31 | else: 32 | ET.SubElement(tr, 'td').text = source.head 33 | dump_container = ET.SubElement(tr, 'td') 34 | ET.SubElement(dump_container, 'a', attrib={ 35 | 'href': '/dumps/' + source.id + '.html' 36 | }).text = source.id 37 | mht_container = ET.SubElement(tr, 'td') 38 | mht_container.text = ', '.join(source.magic_hashtags) 39 | if source.id == 'bot': 40 | mht_container.text = 'N/A' 41 | -------------------------------------------------------------------------------- /Documentation/markdowndoc.py: -------------------------------------------------------------------------------- 1 | """Generates an arbitrary HTML documentation file from a Markdown file""" 2 | 3 | from xml.etree import ElementTree as ET 4 | import markdown 5 | from .generator import Generator 6 | 7 | class MarkdownDoc(Generator): 8 | def __init__(self, mdfile, navi): 9 | with mdfile.open() as mdin: 10 | self.md = mdin.read() 11 | # wrap in so that the XML parser is satisfied 12 | hypertext = '{}'.format(markdown.markdown(self.md)) 13 | generator_kwargs = {'links': navi} 14 | xmltree = ET.fromstring(hypertext) 15 | hl = xmltree.find('./h1') 16 | title = '@_ds_100' 17 | if hl is not None: 18 | title += ': ' + hl.text 19 | # title is overridden by p[@id=meta]/title: 20 | meta = xmltree.find('p[@id="meta"]') 21 | if meta: 22 | xmltree.remove(meta) 23 | titletag = meta.find('./title') 24 | if titletag is not None: 25 | title = titletag.text 26 | desc = meta.find('./desc') 27 | if desc is not None: 28 | generator_kwargs['desc'] = desc.text 29 | super().__init__(title, **generator_kwargs) 30 | self.head.append(hl) 31 | self.navi_in_head() 32 | for tag in xmltree: 33 | if tag.tag == 'h1': 34 | continue 35 | self.main.append(tag) 36 | -------------------------------------------------------------------------------- /Externals/__init__.py: -------------------------------------------------------------------------------- 1 | """External APIs""" 2 | 3 | from .database import setup_database 4 | from .network import set_arguments 5 | from .network import test_network_arguments 6 | from .network import Network 7 | from .mastodon import make_mastodon 8 | 9 | def setup_network(_, args, highest_ids): 10 | return make_mastodon(args, highest_ids) 11 | -------------------------------------------------------------------------------- /Externals/user.py: -------------------------------------------------------------------------------- 1 | """Abstraction of social media users""" 2 | 3 | from urllib.parse import urlparse 4 | 5 | class User: 6 | def __init__(self, name, uid, host): 7 | self._name = name 8 | self._id = uid 9 | self._host = host 10 | 11 | @property 12 | def host(self): 13 | return self._host 14 | 15 | def __str__(self): 16 | return self._name 17 | 18 | def __int__(self): 19 | return self._id 20 | 21 | def __eq__(self, rhs): 22 | return str(self) == str(rhs) 23 | 24 | def fromTwitterUser(twuser): 25 | return User(twuser.screen_name, twuser.id, 'twitter.com') 26 | 27 | def fromMastodonUser(mastuser): 28 | authorurl = urlparse(mastuser.url) 29 | return User(mastuser.acct, mastuser.id, authorurl.hostname) 30 | -------------------------------------------------------------------------------- /Import/__init__.py: -------------------------------------------------------------------------------- 1 | """Importing data from JSON/CSV into Database""" 2 | 3 | import logging 4 | from .sourceconfig import SourceConfig 5 | from .error import SourceError, JsonError, DataError 6 | 7 | log_ = logging.getLogger('setup.' + __name__) 8 | 9 | def find_all_configs(directory): 10 | configurations = {} 11 | data_lists = {} 12 | for f in directory.glob('*.json'): 13 | config = None 14 | try: 15 | config = SourceConfig(f) 16 | except SourceError as se: 17 | log_.critical("Error reading configuation file %s: %s", f, str(se)) 18 | return None 19 | if config.id in configurations: 20 | log_.critical("Source %s specified twice: in %s and in %s", 21 | config.id, configurations[config.id].file, f) 22 | return None 23 | for dl in config.data_list: 24 | if dl.id in data_lists: 25 | log_.critical("Data list %s specified twice: in %s and in %s", 26 | dl.id, data_lists[dl.id], f) 27 | return None 28 | data_lists[dl.id] = config.file 29 | configurations[config.id] = config 30 | return configurations 31 | -------------------------------------------------------------------------------- /Import/access.py: -------------------------------------------------------------------------------- 1 | """Abstracts the source access information (explicit source and magic hashtag)""" 2 | 3 | import logging 4 | from .error import JsonError 5 | log_ = logging.getLogger('setup.' + __name__) 6 | 7 | class Access: 8 | # pylint: disable=R0903 9 | _mandatory_fields = ( 10 | 'type', 11 | 'x_source' 12 | ) 13 | 14 | def __init__(self, config_dict): 15 | self.conf = config_dict 16 | for mf in Access._mandatory_fields: 17 | if mf not in config_dict: 18 | msg = "Key access::{} missing".format(mf) 19 | raise JsonError(msg) 20 | self.type = self.conf['type'] 21 | self.explicit_source = self.conf['x_source'] 22 | -------------------------------------------------------------------------------- /Import/datasource.py: -------------------------------------------------------------------------------- 1 | """Abstracts a data source configuration""" 2 | 3 | import csv 4 | import logging 5 | from .error import DataError, JsonError 6 | from .row import Row 7 | 8 | log_ = logging.getLogger('setup.' + __name__) 9 | 10 | class DataSource: 11 | _mandatory_fields = ( 12 | "file", 13 | "long", 14 | "short", 15 | "source" 16 | ) 17 | 18 | def __init__(self, config_dict, complete_dict): 19 | self.config = config_dict 20 | self.id = self.config.get('id', complete_dict.get('id', None)) 21 | for mf in DataSource._mandatory_fields: 22 | if mf not in config_dict: 23 | msg = "Key {} missing".format(mf) 24 | raise JsonError(msg) 25 | if self.id is None: 26 | raise JsonError("Key id neither in top level nor data::id") 27 | handle = open(self.config['file'], encoding='utf-8') # pylint: disable=consider-using-with 28 | self.reader = csv.DictReader(handle, delimiter=self.config.get('delim', ';')) 29 | self.cols = { 30 | 'short': self.config['short'], 31 | 'long': self.config['long'], 32 | 'add': self.config.get('add', None) 33 | } 34 | self.iter = { 35 | 'iter': None, # iterator 36 | 'split': self.config.get('alias', None), # split character for aliases 37 | 'index': 0, # split array index 38 | 'next': None # next item 39 | } 40 | self.nolink = self.config.get('nolink', False) 41 | self.filters = self.config.get('filter', []) 42 | 43 | def __iter__(self): 44 | self.iter['iter'] = self.reader.__iter__() 45 | return self 46 | 47 | def __next__(self): 48 | row = None 49 | while True: 50 | if self.iter['index'] == 0: 51 | self.iter['next'] = self.iter['iter'].__next__() 52 | try: 53 | row = Row(self.iter, self.cols, self.nolink, self.filters) 54 | if row.valid: 55 | self.iter['index'] = row.next_index() 56 | break 57 | self.iter['index'] = 0 58 | except DataError as de: 59 | de.args = ['{}: {}'.format(self.getPosition(), de.args[0])] 60 | raise 61 | return row 62 | 63 | def getLineNum(self): 64 | return self.reader.line_num 65 | 66 | def getPosition(self): 67 | return "{}::{}".format(self.config['file'], self.line_num) 68 | 69 | line_num = property(getLineNum) 70 | position = property(getPosition) 71 | -------------------------------------------------------------------------------- /Import/error.py: -------------------------------------------------------------------------------- 1 | """Exception classes for imports""" 2 | 3 | class SourceError(Exception): 4 | pass 5 | 6 | class JsonError(SourceError): 7 | pass 8 | 9 | class DataError(SourceError): 10 | pass 11 | -------------------------------------------------------------------------------- /Import/row.py: -------------------------------------------------------------------------------- 1 | """Represents a row of abbreviation data""" 2 | 3 | from AnswerMachine.react import find_tokens 4 | from .error import DataError 5 | 6 | class Row: 7 | def normalize(self, contents, col): 8 | try: 9 | return ' '.join(contents[self.cols[col]].split()) 10 | except AttributeError as ae: 11 | raise DataError(str(ae)) from ae 12 | 13 | # pylint: disable=R0903 14 | def __init__(self, iterator, cols, nolink, filters): 15 | self.cols = cols 16 | for c in ('short', 'long'): 17 | if self.cols[c] not in iterator['next']: 18 | msg = "Column {} ({}) not in contents".format(self.cols[c], c) 19 | raise DataError(msg) 20 | self.abbr = self.split(iterator) 21 | self.long = self.normalize(iterator['next'], 'long') 22 | self.add = None 23 | if self.cols['add'] is not None and self.cols['add'] in iterator['next']: 24 | self.add = iterator['next'][self.cols['add']] 25 | if nolink: 26 | self.long = self.long.replace('.', '\u2024') 27 | self.valid = False 28 | if self.abbr == "": 29 | return # invalid 30 | self.apply_filters(filters, iterator['next']) 31 | if self.valid: 32 | self.check_abbreviation() 33 | 34 | def apply_filters(self, filters, data): 35 | self.filters = filters 36 | if len(self.filters) == 0: 37 | # no filters: everything valid 38 | self.valid = True 39 | for f in self.filters: 40 | if f['col'] not in data: 41 | self.valid = False 42 | continue 43 | string = data[f['col']] 44 | if (string is None) == f['empty']: 45 | self.valid = True 46 | continue 47 | if (string == "") == f['empty']: 48 | self.valid = True 49 | continue 50 | if f['contains'] in string: 51 | self.valid = True 52 | continue 53 | 54 | def split(self, iterator): 55 | short = self.normalize(iterator['next'], 'short') 56 | if iterator['split'] is not None: 57 | abbrs = short.split(iterator['split']) 58 | short = abbrs[iterator['index']] 59 | self.abbr_index = iterator['index'] + 1 60 | if self.abbr_index == len(abbrs): 61 | self.abbr_index = 0 62 | else: 63 | self.abbr_index = 0 64 | return short 65 | 66 | def check_abbreviation(self): 67 | candidates = find_tokens('#' + self.abbr.replace(' ', '_'), '', '') 68 | if len(candidates) != 1: 69 | self.valid = False 70 | msg = "Abbreviation {} will not be matched".format(self.abbr) 71 | raise DataError(msg) 72 | if candidates[0].abbr != self.abbr: 73 | self.valid = False 74 | msg = "Abbreviation {} will be falsely matched as {}".format(self.abbr, 75 | candidates[0].abbr) 76 | raise DataError(msg) 77 | 78 | def next_index(self): 79 | return self.abbr_index 80 | 81 | def __str__(self): 82 | return str(self.__dict__) 83 | -------------------------------------------------------------------------------- /Import/sourceconfig.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """Read source configuation""" 4 | 5 | import json 6 | import logging 7 | from .access import Access 8 | from .datasource import DataSource 9 | from .error import JsonError 10 | log_ = logging.getLogger('setup.' + __name__) 11 | 12 | class SourceConfig: 13 | # pylint: disable=R0903 14 | # pylint: disable=R0902 15 | _mandatory_fields = ( 16 | 'access', 17 | 'data', 18 | 'id', 19 | 'magic_hashtags' 20 | ) 21 | 22 | def __init__(self, filepath): 23 | self.file = filepath 24 | with self.file.open() as jsonfile: 25 | try: 26 | self.json = json.load(jsonfile) 27 | except json.JSONDecodeError as jde: 28 | msg = "{}::{}::{}: JSON object could not be decoded: {}".format( 29 | self.file, jde.lineno, jde.colno, jde.msg) 30 | raise JsonError(msg) from jde 31 | for mf in SourceConfig._mandatory_fields: 32 | if mf not in self.json: 33 | msg = "Key {} missing".format(mf) 34 | raise JsonError(msg) 35 | self.access = [Access(a) for a in self.json['access']] 36 | self.magic_hashtags = self.json['magic_hashtags'] 37 | self.data_list = [DataSource(d, self.json) for d in self.json['data']] 38 | self.id = self.json['id'] 39 | self.head = self.json.get("headline", self.json.get("description", self.id)) 40 | self.desc = self.json.get("description", "") 41 | -------------------------------------------------------------------------------- /Persistence/__init__.py: -------------------------------------------------------------------------------- 1 | """This module contains all functions that make the bot remember things""" 2 | 3 | import logging 4 | 5 | from .gitdescribe import notify_new_version, store_version 6 | from .since import store_since_id, get_since_id 7 | 8 | def init_logger(name='bot'): 9 | logger = logging.getLogger(name) 10 | botformat = logging.Formatter( 11 | fmt="%(levelname)-8s %(name)s %(message)s" 12 | ) 13 | streamhdl = logging.StreamHandler() 14 | streamhdl.setFormatter(botformat) 15 | logger.addHandler(streamhdl) 16 | return logger 17 | 18 | def set_logging_args(parser, default_lvl='CRITICAL'): 19 | levels = "DEBUG INFO WARNING ERROR CRITICAL".split() 20 | parser.add_argument('--log-level', 21 | help='Set logging level', 22 | required=False, 23 | default=default_lvl, 24 | choices=levels) 25 | -------------------------------------------------------------------------------- /Persistence/gitdescribe.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0114 2 | 3 | import logging 4 | 5 | from GitVersion import Git 6 | 7 | logger = logging.getLogger('bot.persistence') 8 | 9 | git_object = Git() 10 | def get_last_hash(sql): 11 | sql.cursor.execute(""" 12 | SELECT 13 | content 14 | FROM 15 | last 16 | WHERE 17 | subject = 'githash' 18 | AND 19 | network = ? 20 | """, (sql.network, )) 21 | row = sql.cursor.fetchone() 22 | if row is None: 23 | return None 24 | return row[0] 25 | 26 | def is_same_version(sql): 27 | return get_last_hash(sql) == git_object.hash() 28 | 29 | def store_version(sql): 30 | for subj, cont in ( 31 | ('gitdescribe', git_object.describe()), 32 | ('githash', git_object.hash()) 33 | ): 34 | # store last answer time 35 | sql.cursor.execute(""" 36 | UPDATE 37 | last 38 | SET 39 | content = ? 40 | WHERE 41 | subject = ? 42 | AND 43 | network = ? 44 | """, 45 | ( 46 | cont, subj, sql.network, 47 | ) 48 | ) 49 | sql.commit() 50 | 51 | def get_changelog(sqlcursor): 52 | last_hash = get_last_hash(sqlcursor) 53 | return git_object.changelog(last_hash).replace('•', '\u200b•') 54 | 55 | def notify_new_version(twitter, database): 56 | if is_same_version(database): 57 | if logger.isEnabledFor(logging.DEBUG): 58 | logger.debug("Using same version as last time: %s", git_object.describe()) 59 | return 60 | status = f"Ich schreibe nun von Version {git_object.describe()}" 61 | cl = get_changelog(database) 62 | if cl.strip() != "": 63 | status += ":\n" + cl 64 | if twitter.post(status): 65 | store_version(database) 66 | -------------------------------------------------------------------------------- /Persistence/since.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions for reading and storing highest ids 3 | """ 4 | 5 | import logging 6 | 7 | logger = logging.getLogger('bot.persistence') 8 | 9 | def get_since_id(sql): 10 | sql.cursor.execute(""" 11 | SELECT 12 | subject, 13 | content 14 | FROM 15 | last 16 | WHERE 17 | subject IN ('since_id', 'since_notification') 18 | AND 19 | network = ? 20 | """, (sql.network, )) 21 | result = dict(sql.cursor.fetchall()) 22 | logger.debug("found highest ids for %s: %s", sql.network, result) 23 | return result 24 | 25 | def store_since_id(sql, network): 26 | id_list = {'since_id': network.high_message} 27 | try: 28 | id_list['since_notification'] = network.high_notification 29 | except AttributeError: 30 | pass 31 | logger.info("storing highest msg id for %s: %s", sql.network, id_list) 32 | # store last answer time 33 | for name, high in id_list.items(): 34 | sql.cursor.execute(""" 35 | UPDATE 36 | last 37 | SET 38 | content = ? 39 | WHERE 40 | subject = ? 41 | AND 42 | network = ? 43 | """, 44 | ( 45 | high, 46 | name, 47 | sql.network, 48 | ) 49 | ) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DS-100-Bot 2 | ========== 3 | 4 | Dies ist ein SocialMedia-Bot zur Expansion von Bahnabkürzungen. Aktuell 5 | können sowohl Twitter als aus Mastodon damit bespielt werden. 6 | 7 | Vorbereitungen 8 | -------------- 9 | 10 | Die Datei config/schema.sql enthält die Struktur der Datenbank. Die 11 | Datenbank ``info.db`` kann damit erstelt werden: 12 | 13 | ``` 14 | cat schema.sql | sqlite3 info.db 15 | ``` 16 | 17 | Um den Twitter-Bot nutzen zu können, muss die geneigte Benutzerin 18 | [Twitter-Developress](https://developer.twitter.com/) werden. Dann 19 | erhält sie auch Authentifizierungsdaten. 20 | 21 | Die Datei credentials.py.dist muss in credentials.py umbenannt werden 22 | und die Twitter-Authentifizierungsdaten eingetragen werden. 23 | 24 | Daten werden eingelesen und die Dokumentation erzeugt mit 25 | 26 | ``` 27 | tools/setup 28 | ``` 29 | 30 | Vorbedinungen 31 | ------------- 32 | 33 | Der Bot ist in python3 geschrieben und benutzt SQLite3 als Datenbank. 34 | Alle verwendeten Python-Packages sind als Ubuntu-Packages verfügbar und 35 | wahrscheinlich auch mit pip installierbar. 36 | 37 | Ausführen 38 | --------- 39 | 40 | Es gibt vier Hauptprogramme. Für Informationen zur Bedienung dieser 41 | Programme kann die Option ``--help`` verwendet werden. 42 | 43 | * ``ds100bot``: Der eigentliche Bot. Kann beliebig oft im Abstand weniger 44 | Minuten ausgeführt werden. 45 | * ``statistics``: Gibt Statistiken über die Benutzung des Bots aus. Sollte 46 | z.B. einmal monatlich ausgeführt werden. 47 | * ``test``: Führt Testfälle aus und überprüft, ob die Testtweets korrekt 48 | beantwortet werden. 49 | * ``get_tweet``: Lädt echte Tweets herunter. Damit können problematische 50 | Tweets genauer analysiert werden. 51 | 52 | LIZENZ 53 | ====== 54 | 55 | Der Quellcode dieses Bots ist unter der Apache Lizenz, Version 2.0, 56 | lizensiert. Siehe Datei LICENSE. 57 | 58 | Die Datei ``config/api_weights.json`` ist von 59 | https://github.com/twitter/twitter-text/tree/master/config/v3.json 60 | genommen und von Twitter, Inc. ebenfalls unter Apache Lizenz, Version 61 | 2.0, lizensiert. 62 | 63 | Die Datentabellen in ``sources`` stehen unter verschiedenen Lizenzen. 64 | Diese sind in ``data`` aufgeführt. 65 | -------------------------------------------------------------------------------- /cards/socialcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baeuchle/ds100bot/1a76be55a87de7793e37ea25d248d1d904843e11/cards/socialcard.png -------------------------------------------------------------------------------- /config/api_weights.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "maxWeightedTweetLength": 280, 4 | "scale": 100, 5 | "defaultWeight": 200, 6 | "emojiParsingEnabled": true, 7 | "transformedURLLength": 23, 8 | "ranges": [ 9 | { 10 | "start": 0, 11 | "end": 4351, 12 | "weight": 100 13 | }, 14 | { 15 | "start": 8192, 16 | "end": 8205, 17 | "weight": 100 18 | }, 19 | { 20 | "start": 8208, 21 | "end": 8223, 22 | "weight": 100 23 | }, 24 | { 25 | "start": 8242, 26 | "end": 8247, 27 | "weight": 100 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /config/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "last" ( 2 | `subject` TEXT NOT NULL, 3 | `content` TEXT, 4 | `network` TEXT NOT NULL 5 | ); 6 | CREATE TABLE IF NOT EXISTS "shortstore" ( 7 | `id` TEXT NOT NULL, 8 | `Abk` TEXT NOT NULL, 9 | `Name` TEXT NOT NULL, 10 | `Kurzname` TEXT, 11 | `Datenliste` TEXT, 12 | `source` TEXT, 13 | PRIMARY KEY(`id`) 14 | ); 15 | CREATE TABLE IF NOT EXISTS "sources" ( 16 | "source_id" TEXT NOT NULL, 17 | "type" TEXT, 18 | "explicit_source" TEXT NOT NULL, 19 | "is_default" INTEGER NOT NULL DEFAULT 0 CHECK(is_default in (0,1)) 20 | ); 21 | CREATE TABLE IF NOT EXISTS "magic_hashtags" ( 22 | "source_id" TEXT NOT NULL, 23 | "magic_hashtag" TEXT NOT NULL 24 | ); 25 | CREATE TABLE IF NOT EXISTS "blacklist" ( 26 | `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 27 | `source` TEXT NOT NULL, 28 | `Abk` TEXT NOT NULL 29 | ); 30 | CREATE TABLE "requestlog" ( 31 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 32 | "explicit_source" TEXT, 33 | "active_magic" TEXT NOT NULL, 34 | "type" TEXT, 35 | "abbreviation" TEXT NOT NULL, 36 | "request_date" TEXT NOT NULL, 37 | "derived_source" TEXT NOT NULL, 38 | "status" TEXT NOT NULL, 39 | "network" TEXT NOT NULL 40 | ); 41 | -------------------------------------------------------------------------------- /data/bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "bot", 3 | "headline": "Informationen über den Bot", 4 | "access": [{ 5 | "x_source": "BOT", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "BOTINFO" 10 | ], 11 | "data": [{ 12 | "id": "bot", 13 | "short": "Abk", 14 | "long": "Name", 15 | "alias": ",", 16 | "file": "sources/bot.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung" 19 | } 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /data/faq.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "faq", 3 | "headline": "FAQs", 4 | "access": [{ 5 | "x_source": "FAQ", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "BOTFAQ" 10 | ], 11 | "data": [{ 12 | "id": "faq", 13 | "short": "Abk", 14 | "long": "Name", 15 | "file": "sources/faq.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | } 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /data/linien_bhvbus.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_bhvbus", 3 | "headline": "Bus Bremerhaven", 4 | "access": [{ 5 | "x_source": "BHV", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_BHV" 10 | ], 11 | "data": [{ 12 | "id": "linien_bhvbus", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_bhvbus.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "FloEllebrecht" 25 | } 26 | }, 27 | "comments": [ 28 | "Stand 2020" 29 | ] 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/linien_bsag.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_bsag", 3 | "headline": "Straßenbahnlinien Bremen", 4 | "access": [{ 5 | "x_source": "HB", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_HB" 10 | ], 11 | "data": [{ 12 | "id": "linien_bsag", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_bsag.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "FloEllebrecht" 25 | } 26 | }, 27 | "comments": [ 28 | "Stand 2020", 29 | "Beinhaltet alle aktuellen Linien", 30 | "Beinhaltet diverse E-Linien (Schülerverkehr, vereinfachte Einsetzer/Aussetzer)", 31 | "Sofern die Nummer aktuell nicht vergeben ist, sind ehemalige Linien vorhanden" 32 | ] 33 | }] 34 | } 35 | -------------------------------------------------------------------------------- /data/linien_by.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_by", 3 | "headline": "ÖPNV Bayern", 4 | "access": [{ 5 | "x_source": "BY", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_BY" 10 | ], 11 | "data": [{ 12 | "id": "linien_by", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_by.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC0", 21 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "SimonBredemeier" 25 | }, 26 | "contributors": [{ 27 | "type": "twitter", 28 | "name": "der_heubi" 29 | }] 30 | } 31 | }] 32 | } 33 | -------------------------------------------------------------------------------- /data/linien_dd.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_dd", 3 | "headline": "Linien Dresden", 4 | "access": [{ 5 | "x_source": "DD", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_DD" 10 | ], 11 | "data": [{ 12 | "id": "linien_dd", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "delim": ",", 16 | "alias": "|", 17 | "file": "sources/linien_dd.csv", 18 | "source": { 19 | "name": "Eigene Zusammenstellung" 20 | }, 21 | "license": { 22 | "name": "CC-BY-SA 4.0", 23 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 24 | "owner": { 25 | "type": "twitter", 26 | "name": "moritzkraehe" 27 | }, 28 | "contributors": [{ 29 | "type": "twitter", 30 | "name": "jonaes_" 31 | }] 32 | } 33 | }] 34 | } 35 | -------------------------------------------------------------------------------- /data/linien_de_fv.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_de_fv", 3 | "headline": "Fernverkehrslinien Deutschland", 4 | "access": [{ 5 | "x_source": "DS", 6 | "type": "/" 7 | }, { 8 | "x_source": "DE", 9 | "type": "/" 10 | }], 11 | "magic_hashtags": [ 12 | "DS100", 13 | "_DE", 14 | "🇩🇪" 15 | ], 16 | "data": [{ 17 | "id": "linien_de_fv", 18 | "short": "Linie", 19 | "long": "Laufweg", 20 | "file": "sources/linien_de_fv.csv", 21 | "source": { 22 | "name": "Eigene Zusammenstellung" 23 | }, 24 | "license": { 25 | "name": "CC0", 26 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 27 | "owner": { 28 | "type": "twitter", 29 | "name": "SimonBredemeier" 30 | } 31 | } 32 | }] 33 | } 34 | -------------------------------------------------------------------------------- /data/linien_delbus.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_delbus", 3 | "headline": "Bus Delmenhorst", 4 | "access": [{ 5 | "x_source": "DEL", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_DEL" 10 | ], 11 | "data": [{ 12 | "id": "linien_delbus", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_delbus.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "FloEllebrecht" 25 | } 26 | }, 27 | "comments": [ 28 | "Stand 2020" 29 | ] 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/linien_ffm.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_ffm", 3 | "headline": "Linien Frankfurt", 4 | "description": "Kommunale Nahverkehrslinien in Frankfurt", 5 | "access": [{ 6 | "x_source": "FFM", 7 | "type": "/" 8 | }], 9 | "magic_hashtags": [ 10 | "_FFM", 11 | "🍎🚋", 12 | "🚋🍎", 13 | "🚋🍏 ", 14 | "🍏🚋" 15 | ], 16 | "data": [{ 17 | "id": "linien_ffm", 18 | "short": "Nummer", 19 | "long": "Laufweg", 20 | "delim": ",", 21 | "alias": ";", 22 | "file": "sources/linien_ffm.csv", 23 | "source": { 24 | "name": "Eigene Zusammenstellung" 25 | }, 26 | "license": { 27 | "name": "Standardlizenz", 28 | "contributors": [{ 29 | "type": "twitter", 30 | "name": "MoritzKraehe" 31 | }], 32 | "contributors": [{ 33 | "type": "twitter", 34 | "name": "jonaes_" 35 | }] 36 | } 37 | }] 38 | } 39 | -------------------------------------------------------------------------------- /data/linien_hg.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_hg", 3 | "headline": "Linien Hochtaunuskreis", 4 | "description": "Buslinien im Hochtaunuskreis", 5 | "access": [{ 6 | "x_source": "HG", 7 | "type": "/" 8 | }], 9 | "magic_hashtags": [ 10 | "_HG" 11 | ], 12 | "data": [{ 13 | "id": "linien_hg", 14 | "short": "Linie", 15 | "long": "Laufweg", 16 | "delim": ",", 17 | "alias": ";", 18 | "file": "sources/linien_hg.csv", 19 | "source": { 20 | "name": "Eigene Zusammenstellung" 21 | }, 22 | "license": { 23 | "name": "CC0", 24 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 25 | "owner": { 26 | "type": "twitter", 27 | "name": "jonaes_" 28 | } 29 | } 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/linien_koeln.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_koeln", 3 | "headline": "Linien Köln", 4 | "description": "Straßenbahnlinien im VRS in Köln", 5 | "access": [{ 6 | "x_source": "KÖ", 7 | "type": "/" 8 | }, { 9 | "x_source": "KOE", 10 | "type": "/" 11 | }], 12 | "magic_hashtags": [ 13 | "_KÖLN", 14 | "_KOELN" 15 | ], 16 | "data": [{ 17 | "short": "Linie", 18 | "long": "Laufweg", 19 | "file": "sources/linien_koeln.csv", 20 | "source": { 21 | "name": "Eigene Zusammenstellung" 22 | }, 23 | "license": { 24 | "name": "CC-BY-SA 3.0", 25 | "url": "https://creativecommons.org/licenses/by-sa/3.0/", 26 | "owner": { 27 | "type": "twitter", 28 | "name": "koelschlenny" 29 | } 30 | } 31 | }] 32 | } 33 | -------------------------------------------------------------------------------- /data/linien_mv.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_mv", 3 | "headline": "SPNV-Linien Mecklenburg-Vorpommern", 4 | "access": [{ 5 | "x_source": "MV", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_MV" 10 | ], 11 | "data": [{ 12 | "id": "linien_mv", 13 | "short": "Linie", 14 | "long": "Name/Laufweg/Betreiber", 15 | "delim": ";", 16 | "file": "sources/linien_mv.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung" 19 | }, 20 | "license": { 21 | "name": "CC-BY-SA 4.0", 22 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 23 | "owner": { 24 | "type": "twitter", 25 | "name": "jonasr_97" 26 | } 27 | } 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /data/linien_ni.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_ni", 3 | "headline": "Regionalbahnlinien Niedersachsen", 4 | "access": [{ 5 | "x_source": "NI", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_NI" 10 | ], 11 | "data": [{ 12 | "id": "linien_ni", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "delim": ";", 16 | "file": "sources/linien_ni.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung" 19 | }, 20 | "license": { 21 | "name": "CC0", 22 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 23 | "owner": { 24 | "type": "twitter", 25 | "name": "SimonBredemeier" 26 | } 27 | } 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /data/linien_nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_nl", 3 | "headline": "SPV Niederlande", 4 | "access": [{ 5 | "x_source": "NL", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_NL", 10 | "🇳🇱" 11 | ], 12 | "data": [{ 13 | "id": "linien_nl", 14 | "short": "Linie", 15 | "long": "Laufweg", 16 | "file": "sources/linien_nl.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung" 19 | }, 20 | "license": { 21 | "name": "CC0", 22 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 23 | "owner": { 24 | "type": "twitter", 25 | "name": "SimonBredemeier" 26 | } 27 | } 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /data/linien_no.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_no", 3 | "headline": "SPV Norwegen", 4 | "access": [{ 5 | "x_source": "NO", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_NO", 10 | "🇳🇴" 11 | ], 12 | "data": [{ 13 | "id": "linien_no", 14 | "short": "Linje", 15 | "long": "Vei", 16 | "file": "sources/linien_no.csv", 17 | "source": { 18 | "name": "Liniennummernschema von 2023" 19 | }, 20 | "comments": [ 21 | "Stand: 2023" 22 | ] 23 | }] 24 | } 25 | -------------------------------------------------------------------------------- /data/linien_nvs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_nvs", 3 | "headline": "ÖPNV-Linien in Schwerin", 4 | "description": "ÖPNV-Linien im Stadtgebiet Potsdam außer Bahn-Verkehr", 5 | "access": [{ 6 | "x_source": "SCHWERIN", 7 | "type": "/" 8 | }], 9 | "magic_hashtags": [ 10 | "_SCHWERIN" 11 | ], 12 | "data": [{ 13 | "id": "linien_nvs", 14 | "short": "Linie", 15 | "long": "Laufweg", 16 | "file": "sources/linien_nvs.csv", 17 | "source": { 18 | "name": "Straßenbahn- und Buslinien der Nahverkehr Schwerin GmbH; Eigene Zusammenstellung auf Basis der Webseite" 19 | }, 20 | "license": { 21 | "name": "CC-BY-SA 4.0", 22 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 23 | "owner": { 24 | "type": "twitter", 25 | "name": "jonasr_97" 26 | } 27 | } 28 | }, { 29 | "id": "linien_vlp", 30 | "short": "Linie", 31 | "long": "Laufweg", 32 | "file": "sources/linien_vlp.csv", 33 | "source": { 34 | "name": "Buslinien der Verkehrsgesellschaft Ludwigslust-Parchim mbH; Eigene Zusammenstellung auf Basis der Webseite" 35 | }, 36 | "license": { 37 | "name": "CC-BY-SA 4.0", 38 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 39 | "owner": { 40 | "type": "twitter", 41 | "name": "jonasr_97" 42 | } 43 | } 44 | }, { 45 | "id": "linien_nwm", 46 | "short": "Linie", 47 | "long": "Laufweg", 48 | "file": "sources/linien_nwm.csv", 49 | "source": { 50 | "name": "Buslinien der NAHBUS Nordwestmecklenburg GmbH; Eigene Zusammenstellung auf Basis der Webseite" 51 | }, 52 | "license": { 53 | "name": "CC-BY-SA 4.0", 54 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 55 | "owner": { 56 | "type": "twitter", 57 | "name": "jonasr_97" 58 | } 59 | } 60 | }] 61 | } 62 | -------------------------------------------------------------------------------- /data/linien_nw.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_nw", 3 | "headline": "Regionalbahnlinien Nordrhein-Westfalen", 4 | "access": [{ 5 | "x_source": "NW", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_NW" 10 | ], 11 | "data": [{ 12 | "id": "linien_nw", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "delim": ";", 16 | "file": "sources/linien_nw.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung auf Basis des NRW Fahrplanbuch 2022", 19 | "url": "https://www.mobil.nrw/fileadmin/01_Content_Sales_Hub/Downloadcenter/NRW_Fahrplanbuch_2022.pdf" 20 | }, 21 | "license": { 22 | "name": "CC0", 23 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 24 | "owner": { 25 | "type": "twitter", 26 | "name": "C1710" 27 | } 28 | }, 29 | "comments": [ 30 | "Stand: Januar 2022" 31 | ] 32 | }] 33 | } 34 | -------------------------------------------------------------------------------- /data/linien_opr.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_opr", 3 | "headline": "Buslinien im Landkreis Ostprignitz-Ruppin ", 4 | "description": "Buslinien, die von der Ostprignitz-Ruppiner Personennahverkehrsgesellschaft mbH betrieben werden", 5 | "access": [{ 6 | "x_source": "OPR", 7 | "type": "/" 8 | }], 9 | "magic_hashtags": [ 10 | "_OPR" 11 | ], 12 | "data": [{ 13 | "id": "linien_opr", 14 | "short": "Linie", 15 | "long": "Laufweg", 16 | "file": "sources/linien_opr.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung auf Basis von orp-busse.de", 19 | "url": "https://www.orp-busse.de" 20 | }, 21 | "license": { 22 | "name": "CC-BY-SA 4.0", 23 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 24 | "owner": { 25 | "type": "twitter", 26 | "name": "jonasr_97" 27 | } 28 | } 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /data/linien_p.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_p", 3 | "headline": "ÖPNV-Linien in Potsdam", 4 | "description": "ÖPNV-Linien im Stadtgebiet Potsdam außer Bahn-Verkehr", 5 | "access": [{ 6 | "x_source": "P", 7 | "type": "/" 8 | }], 9 | "magic_hashtags": [ 10 | "_P" 11 | ], 12 | "data": [{ 13 | "id": "linien_p", 14 | "short": "Linie", 15 | "long": "Laufweg", 16 | "file": "sources/linien_p.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung auf Basis von: Liniennetz Potsdam & Websites der Verkehrsunternehmen ViP, regiobus, VTF, HVG, BVG" 19 | }, 20 | "license": { 21 | "name": "CC-BY-SA 4.0", 22 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 23 | "owner": { 24 | "type": "twitter", 25 | "name": "jonasr_97" 26 | } 27 | } 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /data/linien_rmv.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_rmv", 3 | "headline": "SPNV Rhein-Main-Verkehrsverbund", 4 | "access": [{ 5 | "x_source": "RMV", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_RMV" 10 | ], 11 | "data": [{ 12 | "id": "linien_rmv", 13 | "short": "Liniennummer", 14 | "long": "Name, Laufweg, Betreiber", 15 | "file": "sources/linien_rmv.csv", 16 | "comments": [ 17 | "Die Liniennamen stammen aus dem Kursbuch des RMV" 18 | ], 19 | "source": { 20 | "name": "Eigene Zusammenstellung" 21 | }, 22 | "license": { 23 | "name": "CC0", 24 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 25 | "owner": { 26 | "type": "twitter", 27 | "name": "jonaes_" 28 | } 29 | } 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/linien_rnv.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_rnv", 3 | "headline": "Straßenbahnlinien des RNV", 4 | "access": [{ 5 | "x_source": "RNV", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_RNV" 10 | ], 11 | "data": [{ 12 | "id": "linien_rnv", 13 | "short": "Nummer", 14 | "long": "Laufweg", 15 | "file": "sources/linien_rnv.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "moritzkraehe" 25 | } 26 | } 27 | }] 28 | } 29 | -------------------------------------------------------------------------------- /data/linien_rvf.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_rvf", 3 | "headline": "Straßenbahnlinien Freiburg", 4 | "access": [{ 5 | "x_source": "RVF", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_RVF" 10 | ], 11 | "data": [{ 12 | "id": "linien_rvf", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_rvf.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "FloEllebrecht" 25 | }, 26 | "contributors": [{ 27 | "type": "twitter", 28 | "name": "trassenfinder" 29 | }] 30 | }, 31 | "comments": [ 32 | "Stand 2020", 33 | "Enthält alle Linien des RVF", 34 | "Nachtlinien fangen mit N an", 35 | "Bürgerbus Breisach fangen mit BB an", 36 | "Linien aus Emmendingen fangen mit EN an", 37 | "Außerdem: NB für Neuenburg, SB für Stadtbus Breisach" 38 | ] 39 | }] 40 | } 41 | -------------------------------------------------------------------------------- /data/linien_sh.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_sh", 3 | "headline": "Regionalbahnlinien Schleswig-Holstein", 4 | "access": [{ 5 | "x_source": "SH", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_SH" 10 | ], 11 | "data": [{ 12 | "id": "linien_sh", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "delim": ";", 16 | "file": "sources/linien_sh.csv", 17 | "source": { 18 | "name": "Eigene Zusammenstellung" 19 | }, 20 | "license": { 21 | "name": "CC0", 22 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 23 | "owner": { 24 | "type": "twitter", 25 | "name": "SimonBredemeier" 26 | } 27 | } 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /data/linien_sn.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_sn", 3 | "headline": "Linien Sachsen", 4 | "access": [{ 5 | "x_source": "SN", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_SN" 10 | ], 11 | "data": [{ 12 | "id": "linien_sn", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "delim": ",", 16 | "alias": "|", 17 | "file": "sources/linien_sn.csv", 18 | "source": { 19 | "name": "Eigene Zusammenstellung" 20 | }, 21 | "license": { 22 | "name": "CC-BY-SA 4.0", 23 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 24 | "owner": { 25 | "type": "twitter", 26 | "name": "moritzkraehe" 27 | } 28 | } 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /data/linien_th.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_th", 3 | "headline": "SPNV Thüringen", 4 | "access": [{ 5 | "x_source": "TH", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_TH" 10 | ], 11 | "data": [{ 12 | "id": "linien_th", 13 | "short": "Linie", 14 | "long": "Laufweg und Betreiber", 15 | "file": "sources/linien_th.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC0", 21 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "jonaes_" 25 | }, 26 | "contributors": [{ 27 | "type": "twitter", 28 | "name": "SimonBredemeier" 29 | }] 30 | } 31 | }] 32 | } 33 | -------------------------------------------------------------------------------- /data/linien_vab.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_vab", 3 | "headline": "Buslinien Aschaffenburg", 4 | "access": [{ 5 | "x_source": "VAB", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_VAB" 10 | ], 11 | "data": [{ 12 | "id": "linien_vab_sa", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_vab_stadt_ab.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC0", 21 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "BlubbBlubber3" 25 | } 26 | }, 27 | "comments": [ 28 | "Buslinien der Stadt Aschaffenburg" 29 | ] 30 | },{ 31 | "id": "linien_vab_ka", 32 | "short": "Linie", 33 | "long": "Laufweg", 34 | "file": "sources/linien_vab_kreis_ab.csv", 35 | "source": { 36 | "name": "Eigene Zusammenstellung" 37 | }, 38 | "license": { 39 | "name": "CC0", 40 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 41 | "owner": { 42 | "type": "twitter", 43 | "name": "BlubbBlubber3" 44 | } 45 | }, 46 | "comments": [ 47 | "Buslinien des Kreises Aschaffenburg" 48 | ] 49 | },{ 50 | "id": "linien_vab_km", 51 | "short": "Linie", 52 | "long": "Laufweg", 53 | "file": "sources/linien_vab_kreis_mb.csv", 54 | "source": { 55 | "name": "Eigene Zusammenstellung" 56 | }, 57 | "license": { 58 | "name": "CC0", 59 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 60 | "owner": { 61 | "type": "twitter", 62 | "name": "BlubbBlubber3" 63 | } 64 | }, 65 | "comments": [ 66 | "Buslinien des Kreises Miltenberg" 67 | ] 68 | }] 69 | } 70 | -------------------------------------------------------------------------------- /data/linien_vbb.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_vbb", 3 | "headline": "SPNV-Linien Berlin/Brandenburg", 4 | "access": [{ 5 | "x_source": "VBB", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_VBB", 10 | "🧸" 11 | ], 12 | "data": [{ 13 | "id": "linien_vbb", 14 | "short": "Linie", 15 | "long": "Bezeichnung/Laufweg/Betreiber", 16 | "delim": ";", 17 | "file": "sources/linien_vbb.csv", 18 | "source": { 19 | "name": "Eigene Zusammenstellung" 20 | }, 21 | "license": { 22 | "name": "CC-BY-SA 4.0", 23 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 24 | "owner": { 25 | "type": "twitter", 26 | "name": "jonasr_97" 27 | }, 28 | "contributors": [{ 29 | "type": "twitter", 30 | "name": "jonaes_" 31 | }] 32 | } 33 | }] 34 | } 35 | -------------------------------------------------------------------------------- /data/linien_vvs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_vvs", 3 | "headline": "ÖPNV-Linien Stuttgart", 4 | "access": [{ 5 | "x_source": "STG", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_STG" 10 | ], 11 | "data": [{ 12 | "id": "linien_vvs", 13 | "short": "Linie", 14 | "long": "Streckenverlauf", 15 | "file": "sources/linien_vvs.csv", 16 | "alias": ",", 17 | "source": { 18 | "name": "VVS Open Data Portal", 19 | "url": "https://www.openvvs.de/dataset/linien" 20 | }, 21 | "license": { 22 | "name": "CC-BY 4.0", 23 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 24 | "owner": { 25 | "type": "link", 26 | "name": "Verkehrs- und Tarifverbund Stuttgart", 27 | "url": "https://vvs.de" 28 | } 29 | }, 30 | "comments": [ 31 | "Stand 2019" 32 | ] 33 | }] 34 | } 35 | -------------------------------------------------------------------------------- /data/linien_wtv.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "linien_wtv", 3 | "headline": "Bus Waldshuter TV", 4 | "access": [{ 5 | "x_source": "WTV", 6 | "type": "/" 7 | }], 8 | "magic_hashtags": [ 9 | "_WTV" 10 | ], 11 | "data": [{ 12 | "id": "linien_wtv", 13 | "short": "Linie", 14 | "long": "Laufweg", 15 | "file": "sources/linien_wtv.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "FloEllebrecht" 25 | } 26 | }, 27 | "comments": [ 28 | "Stand 2020" 29 | ] 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/orte_at.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_at", 3 | "headline": "Betriebsstellen in Österreich", 4 | "access": [{ 5 | "x_source": "AT", 6 | "type": "#" 7 | },{ 8 | "x_source": "ÖBB", 9 | "type": "#" 10 | }], 11 | "magic_hashtags": [ 12 | "_AT", 13 | "DB640", 14 | "ÖBB", 15 | "🇦🇹" 16 | ], 17 | "data": [{ 18 | "id": "orte_at", 19 | "short": "Abk", 20 | "long": "Name", 21 | "delim": ";", 22 | "nolink": true, 23 | "file": "sources/orte_at_db640.csv", 24 | "source": { 25 | "name": "Betriebsstellen Österreich laut DB 640", 26 | "modified": true 27 | }, 28 | "comments": [ 29 | "Quelle ist nicht offiziell.", 30 | "Groß-/Kleinschreibung muss beachtet werden, es sind auch Kleinbuchstaben erlaubt!" 31 | ] 32 | }] 33 | } 34 | -------------------------------------------------------------------------------- /data/orte_be.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_be", 3 | "description": "Telegraph Codes for Belgian railway stations^", 4 | "headline": "Betriebsstellen Belgien", 5 | "access": [{ 6 | "x_source": "BE", 7 | "type": "#" 8 | }], 9 | "magic_hashtags": [ 10 | "_BE", 11 | "🇧🇪" 12 | ], 13 | "data": [{ 14 | "id": "orte_de", 15 | "short": "telegraph-code", 16 | "long": "name", 17 | "delim": ",", 18 | "nolink": true, 19 | "file": "sources/orte_be.csv", 20 | "description": "Telegraph Codes", 21 | "source": { 22 | "name": "Open transport data in Belgium", 23 | "url": "https://github.com/iRail/stations/pull/166/commits", 24 | "modified": false 25 | }, 26 | "license": { 27 | "name": "CC-0", 28 | "url": "https://creativecommons.org/publicdomain/zero/1.0/" 29 | } 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/orte_ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_ca", 3 | "headline": "Betriebsstellen Kanada", 4 | "access": [{ 5 | "x_source": "CA", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_CA", 10 | "🇨🇦" 11 | ], 12 | "data": [{ 13 | "id": "orte_ca_via", 14 | "short": "Station Code", 15 | "long": "Station Name", 16 | "delim": ";", 17 | "nolink": true, 18 | "file": "sources/orte_ca_via.csv", 19 | "description": "Bahnhofskürzel der VIA Rail Canada", 20 | "source": { 21 | "name": "Eigene Zusammenstellung aus OSM-Daten" 22 | } 23 | }] 24 | } 25 | -------------------------------------------------------------------------------- /data/orte_ch.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_ch", 3 | "headline": "Betriebsstellen in der Schweiz", 4 | "access": [{ 5 | "x_source": "CH", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_CH", 10 | "🇨🇭" 11 | ], 12 | "data": [{ 13 | "id": "orte_ch", 14 | "short": "Abkuerzung", 15 | "long": "BEZEICHNUNG_OFFIZIELL", 16 | "delim": ";", 17 | "file": "sources/orte_ch_didok.csv", 18 | "source": { 19 | "name": "Haltestellen des öffentlichen Verkehrs", 20 | "url": "https://opendata.swiss/de/dataset/haltestellen-des-offentlichen-verkehrs", 21 | "modified": true 22 | }, 23 | "license": { 24 | "name": "ähnlich CC-BY", 25 | "url": "https://opendata.swiss/de/dataset?q=haltestelle&organization=bundesamt-fur-verkehr-bav&res_rights=NonCommercialAllowed-CommercialAllowed-ReferenceRequired", 26 | "owner": { 27 | "type": "name", 28 | "name": "SBB" 29 | } 30 | }, 31 | "comments": [ 32 | "Aus der der Originalquelle sind nur diejenigen Einträge übernommen, die wirklich eine Abkürzung haben." 33 | ] 34 | }, { 35 | "id": "orte_ch_add", 36 | "short": "Abk", 37 | "long": "Name", 38 | "file": "sources/orte_ch_add.csv", 39 | "source": { 40 | "name": "Eigene Zusammenstellung" 41 | }, 42 | "license": { 43 | "name": "Standardlizenz", 44 | "contributors": [{ 45 | "type": "twitter", 46 | "name": "Stechny" 47 | },{ 48 | "type": "twitter", 49 | "name": "TWeinlein" 50 | },{ 51 | "type": "twitter", 52 | "name": "danihaenni" 53 | }] 54 | }, 55 | "comments": [ 56 | "Zusätzliche Kürzel aus privaten Informationen" 57 | ] 58 | }] 59 | } 60 | -------------------------------------------------------------------------------- /data/orte_dd.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_dd", 3 | "headline": "Haltestellen Dresden", 4 | "access": [{ 5 | "x_source": "DD", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_DD" 10 | ], 11 | "data": [{ 12 | "id": "orte_dd", 13 | "short": "Abkürzung", 14 | "long": "Haltestelle", 15 | "delim": ";", 16 | "nolink": true, 17 | "file": "sources/orte_dd.csv", 18 | "description": "Haltestellen Dresden", 19 | "source": { 20 | "name": "Haltestellenkürzel der Dresdener Verkehrsbetriebe AG", 21 | "url": "https://www.dvb.de/de-de/fahrplan/haltestellenauskunft/haltestellenkuerzel/" 22 | } 23 | }] 24 | } 25 | -------------------------------------------------------------------------------- /data/orte_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_de", 3 | "description": "Eisenbahnbetriebsstellen, Bahnhöfe und Haltepunkte in Deutschland", 4 | "headline": "Betriebsstellen Deutschland", 5 | "access": [{ 6 | "x_source": "DS", 7 | "type": "#" 8 | }, { 9 | "x_source": "DE", 10 | "type": "#" 11 | }], 12 | "magic_hashtags": [ 13 | "DS100", 14 | "_DE", 15 | "🇩🇪" 16 | ], 17 | "data": [{ 18 | "id": "ds100", 19 | "short": "RL100-Code", 20 | "long": "RL100-Langname", 21 | "delim": ";", 22 | "nolink": true, 23 | "file": "sources/orte_de_ds100.csv", 24 | "description": "Betriebsstellen der Deutschen Bahn nach Ril100", 25 | "source": { 26 | "name": "Betriebsstellenverzeichnis der Deutschen Bahn AG", 27 | "url": "https://data.deutschebahn.com/dataset/data-betriebsstellen", 28 | "modified": true 29 | }, 30 | "license": { 31 | "name": "CC-BY 4.0", 32 | "url": "https://creativecommons.org/licenses/by/4.0/", 33 | "owner": { 34 | "type": "name", 35 | "name": "Deutsche Bahn AG" 36 | } 37 | }, 38 | "comments": [ 39 | "Stand 07/2021" 40 | ] 41 | }, { 42 | "id": "orte_de_add", 43 | "short": "Abk", 44 | "long": "Name", 45 | "file": "sources/orte_de_add.csv", 46 | "source": { 47 | "name": "Eigene Zusammenstellung" 48 | }, 49 | "comments": [ 50 | "Zusätzliche Kürzel aus privaten Informationen" 51 | ] 52 | }] 53 | } 54 | -------------------------------------------------------------------------------- /data/orte_dk.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_dk", 3 | "headline": "Betriebsstellen Dänemark", 4 | "access": [{ 5 | "x_source": "DK", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_DK", 10 | "🇩🇰" 11 | ], 12 | "data": [{ 13 | "id": "orte_dk", 14 | "short": "FORKORTELSE", 15 | "long": "NAVN", 16 | "delim": ";", 17 | "nolink": true, 18 | "file": "sources/orte_dk.csv", 19 | "description": "Betriebsstellen Dänemark", 20 | "source": { 21 | "name": "Strækningsregister", 22 | "url": "https://www.bane.dk/da/Leverandoer/Krav/Tekniske-data", 23 | "modified": true 24 | 25 | }, 26 | "filter": [{ 27 | "col": "FILTER", 28 | "contains": "STED", 29 | "empty": true 30 | }], 31 | "comments": [ 32 | "Aus der Gesamtliste sind die Einträge gefiltert, die keinen Bindestrich enthalten (Formel =WENNFEHLER(FINDEN(\"-\",G2,1);\"STED\"))", 33 | "Für Grenaa Havn wird aus technischen Gründen #DK:Gr_H statt '#DK:Gr.H' verwendet." 34 | ] 35 | }] 36 | } 37 | -------------------------------------------------------------------------------- /data/orte_ffm.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_ffm", 3 | "headline": "Haltestellen Frankfurt", 4 | "access": [{ 5 | "x_source": "FFM", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_FFM", 10 | "🍎🚋", 11 | "🚋🍎", 12 | "🚋🍏 ", 13 | "🍏🚋" 14 | ], 15 | "data": [{ 16 | "id": "orte_ffm", 17 | "short": "Abk", 18 | "long": "Name", 19 | "delim": ";", 20 | "file": "sources/orte_ffm.csv", 21 | "source": { 22 | "name": "Eigene Zusammenstellung, Hilfe aus privaten Nachrichten und dem Frankfurter Nahverkehrsforum", 23 | "url": "https://frankfurter-nahverkehrsforum.de/forum/index.php?thread/20682-stationskürzel/", 24 | "modified": true 25 | }, 26 | "comments": [ 27 | "Alle Stadtbahn- und von der Leitstelle betreuten Straßenbahnhaltestellen haben ein Kürzel aus zwei Buchstaben", 28 | "Bei Kreuzungsbahnhöfen im Stadtbahnbereich hat jede Ebene einen dritten Buchstaben", 29 | "Andere Betriebsstellen haben längere Kürzel.", 30 | "Alle Stadtbahnhaltestellen haben dreistellige Nummern", 31 | "Straßenbahnhaltestellen haben vierstellige Nummern (diese sind allerdings nur lückenhaft bekannt)", 32 | "Die Kürzel für Wendeanlagen fangen mit einem Unterstrich an '_'. Dies kann mit Magic Hashtags kollidieren (und tut es mindestens bei _HB), daher sollten diese nur voll qualifiziert benutzt werden, also als '#FFM:_HB'." 33 | ] 34 | }] 35 | } 36 | -------------------------------------------------------------------------------- /data/orte_fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_fr", 3 | "headline": "Betriebsstellen in Frankreich", 4 | "access": [{ 5 | "x_source": "FR", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_FR", 10 | "🇫🇷" 11 | ], 12 | "data": [{ 13 | "id": "orte_fr", 14 | "short": "Abréviation", 15 | "long": "Définition", 16 | "delim": ";", 17 | "file": "sources/orte_sncf.csv", 18 | "source": { 19 | "name": "Lexique des abréviations SNCF", 20 | "url": "https://ressources.data.sncf.com/explore/dataset/lexique-des-acronymes-sncf/", 21 | "modified": true 22 | }, 23 | "license": { 24 | "name": "ODbL", 25 | "url": "https://opendatacommons.org/licenses/odbl/", 26 | "owner": { 27 | "type": "name", 28 | "name": "SNCF" 29 | } 30 | }, 31 | "comments": [ 32 | "Scheinbar war die Originalquelle früher all-caps und wird langsam umgestellt. Das ist aber nur bis C oder D gekommen, danach wird's etwas uneinheitlich." 33 | ] 34 | }] 35 | } 36 | -------------------------------------------------------------------------------- /data/orte_hh.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_hamburg", 3 | "headline": "Haltestellen Hochbahn Hamburg", 4 | "access": [{ 5 | "x_source": "HH", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_HH", 10 | "🍔" 11 | ], 12 | "data": [{ 13 | "id": "orte_hh", 14 | "short": "Abk", 15 | "long": "Name", 16 | "delim": ";", 17 | "file": "sources/orte_hamburg.csv", 18 | "source": { 19 | "name": "Hamburger-Bahnhöfe.de", 20 | "url": "http://hamburger-bahnhoefe.de/" 21 | }, 22 | "comments": [ 23 | "Privat zusammengestellte Liste" 24 | ] 25 | }] 26 | } 27 | -------------------------------------------------------------------------------- /data/orte_ibnr.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_ibnr", 3 | "headline": "Bahnhofsnummern Deutschland", 4 | "access": [{ 5 | "x_source": "IBNR", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "IBNR", 10 | "🚉" 11 | ], 12 | "data": [{ 13 | "id": "orte_de_stationsdaten", 14 | "short": "Bf. Nr.", 15 | "long": "Station", 16 | "file": "sources/orte_de_stationsdaten.csv", 17 | "source": { 18 | "name": "Stationsdaten", 19 | "url": "https://data.deutschebahn.com/dataset/data-stationsdaten" 20 | }, 21 | "license": { 22 | "name": "CC-BY 4.0", 23 | "url": "https://creativecommons.org/licenses/by/4.0/", 24 | "owner": { 25 | "type": "name", 26 | "name": "Deutsche Bahn AG" 27 | } 28 | }, 29 | "comments": [ 30 | "Stand 03/2020", 31 | "Stationsnummern" 32 | ] 33 | }] 34 | } 35 | -------------------------------------------------------------------------------- /data/orte_leitpunkte.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_leitpunkte", 3 | "headline": "Leitpunkte Deutschland", 4 | "description": "Wegepunkte auf Fahrkartencodes", 5 | "access": [{ 6 | "x_source": "LP", 7 | "type": "#" 8 | }], 9 | "magic_hashtags": [ 10 | "_LP" 11 | ], 12 | "data": [{ 13 | "id": "leitpunkte", 14 | "short": "Abk", 15 | "long": "Name", 16 | "delim": ";", 17 | "file": "sources/orte_leitpunkte.csv", 18 | "source": { 19 | "name": "Tarifpunkte in den Beförderungsbedingungen der DB", 20 | "url": "https://www.bahn.de/p/view/mdb/bahnintern/agb/entfernungswerk/mdb_305971_teil_2-3_tarifpunkte_anstobahnhfe_regionen.pdf", 21 | "modified": false 22 | }, 23 | "license": { 24 | "name": " ", 25 | "owner": { 26 | "type": "name", 27 | "name": "Deutsche Bahn AG" 28 | } 29 | } 30 | }] 31 | } 32 | -------------------------------------------------------------------------------- /data/orte_nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_nl", 3 | "headline": "Betriebsstellen Niederlande", 4 | "access": [{ 5 | "x_source": "NL", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_NL", 10 | "🇳🇱" 11 | ], 12 | "data": [{ 13 | "id": "orte_nl", 14 | "short": "Abk", 15 | "long": "Name", 16 | "delim": ";", 17 | "file": "sources/orte_nl.csv", 18 | "source": { 19 | "name": "Anhang 6 zu Regeling spoorverkeer", 20 | "url": "https://wetten.overheid.nl/BWBR0017707/2020-04-01/#Bijlage6" 21 | }, 22 | "license": { 23 | "name": "Gemeinfrei, da Gesetz" 24 | }, 25 | "comments": [ 26 | "Stand 2020" 27 | ] 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /data/orte_no.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_no", 3 | "description": "Eisenbahnbetriebsstellen Norwegen", 4 | "headline": "Betriebsstellen Norwegen", 5 | "access": [{ 6 | "x_source": "NO", 7 | "type": "#" 8 | }], 9 | "magic_hashtags": [ 10 | "_NO", 11 | "🇳🇴" 12 | ], 13 | "data": [{ 14 | "id": "orte_no", 15 | "short": "Fork", 16 | "long": "Navn", 17 | "delim": ";", 18 | "nolink": true, 19 | "file": "sources/orte_no.csv", 20 | "description": "Betriebsstellen Norwegen", 21 | "source": { 22 | "name": "Grafische Fahrpläne, Stand 2020", 23 | "url": "https://www.banenor.no/kundeportal/ruter-og-sportilgang/grafiske-togruter1/", 24 | "modified": true 25 | }, 26 | "comments": [ 27 | "Selbst abgetippt" 28 | ] 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /data/orte_nvs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_nvs", 3 | "headline": "Haltestellen vom Nahverkehr Schwerin", 4 | "access": [{ 5 | "x_source": "SCHWERIN", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_SCHWERIN" 10 | ], 11 | "data": [{ 12 | "id": "orte_nvs", 13 | "short": "Abkürzung", 14 | "long": "Haltestelle", 15 | "file": "sources/orte_nvs.csv", 16 | "source": { 17 | "name": "Eigene Zusammenstellung auf Basis von: Website Nahverkehr Schwerin (NVS)" 18 | }, 19 | "license": { 20 | "name": "CC-BY-SA 4.0", 21 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 22 | "owner": { 23 | "type": "twitter", 24 | "name": "jonasr_97" 25 | } 26 | } 27 | }] 28 | } 29 | -------------------------------------------------------------------------------- /data/orte_se.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_se", 3 | "headline": "Betriebsstellen Schweden", 4 | "access": [{ 5 | "x_source": "SE", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_SE", 10 | "🇸🇪" 11 | ], 12 | "data": [{ 13 | "id": "orte_se", 14 | "short": "Abk", 15 | "long": "Name", 16 | "file": "sources/orte_se.csv", 17 | "source": { 18 | "name": "Svensk Wikipedia", 19 | "url": "https://sv.wikipedia.org/wiki/Lista_över_trafikplatssignaturer_i_det_svenska_järnvägsnätet" 20 | }, 21 | "license": { 22 | "name": "CC-BY-SA 4.0", 23 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 24 | "owner": { 25 | "type": "twitter", 26 | "name": "FloEllebrecht" 27 | } 28 | } 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /data/orte_uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_uk", 3 | "headline": "Betriebsstellen Großbritannien", 4 | "access": [{ 5 | "x_source": "UK", 6 | "type": "#" 7 | },{ 8 | "x_source": "GB", 9 | "type": "#" 10 | }], 11 | "magic_hashtags": [ 12 | "_UK", 13 | "_GB", 14 | "🇬🇧" 15 | ], 16 | "data": [{ 17 | "id": "orte_uk_crs", 18 | "short": "CRS Code", 19 | "long": "Station Name", 20 | "delim": ",", 21 | "file": "sources/orte_uk_crs.csv", 22 | "source": { 23 | "name": "National Rail Enquiries", 24 | "url": "https://www.nationalrail.co.uk/stations_destinations/48541.aspx", 25 | "modified": true 26 | }, 27 | "comments": [ 28 | "Dreistellige Kürzel", 29 | "Stand 2018" 30 | ] 31 | },{ 32 | "id": "orte_uk_long", 33 | "short": "Abbr", 34 | "long": "Name", 35 | "delim": ";", 36 | "file": "sources/orte_uk_long.csv", 37 | "source": { 38 | "name": "Rail Delivery Group", 39 | "url": "http://data.atoc.org/how-to", 40 | "modified": true 41 | }, 42 | "license": { 43 | "name": "CC-BY 4.0", 44 | "url": "https://creativecommons.org/licenses/by/4.0/", 45 | "owner": { 46 | "type": "name", 47 | "name": "Rail Delivery Group" 48 | } 49 | }, 50 | "comments": [ 51 | "Detaillierte Betriebsstellen", 52 | "Aus Fixed-width-Text extrahiert", 53 | "Namen mit Kleinbuchstaben versehen", 54 | "4- bis 7-stellige Kürzel", 55 | "Stand 2020" 56 | ] 57 | }] 58 | } 59 | -------------------------------------------------------------------------------- /data/orte_wien.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "orte_wien", 3 | "headline": "Haltestellen Wien", 4 | "access": [{ 5 | "x_source": "W", 6 | "type": "#" 7 | }], 8 | "magic_hashtags": [ 9 | "_W" 10 | ], 11 | "data": [{ 12 | "id": "orte_wien", 13 | "short": "Abk", 14 | "long": "Name", 15 | "delim": ";", 16 | "file": "sources/orte_wien.csv", 17 | "source": { 18 | "name": "Christoph Schönweilers hauptsignal.at", 19 | "url": "https://bahn.hauptsignal.at/" 20 | }, 21 | "comments": [ 22 | "Datenbanksuche auf hauptsignal.at", 23 | "Stand 2020" 24 | ] 25 | }] 26 | } 27 | -------------------------------------------------------------------------------- /data/regeln_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "regeln_de", 3 | "headline": "Eisenbahnbegriffe (Deutschland)", 4 | "description": "Abkürzungen aus Betriebsregelwerk des VDV und Ril 408 der Deutschen Bahn", 5 | "access": [{ 6 | "x_source": "DS", 7 | "type": "&" 8 | }], 9 | "magic_hashtags": [ 10 | "DS100", 11 | "_DE", 12 | "DS301", 13 | "_SH", 14 | "_NI", 15 | "_RMV", 16 | "DV301", 17 | "_TH", 18 | "🇩🇪" 19 | ], 20 | "data": [{ 21 | "id": "ril408", 22 | "short": "Abk", 23 | "long": "Name", 24 | "alias": ",", 25 | "nolink": true, 26 | "file": "sources/regeln_de.csv", 27 | "source": { 28 | "name": "Eigene Zusammenstellung" 29 | }, 30 | "license": { 31 | "name": "CC-BY-SA 4.0", 32 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 33 | "owner": { 34 | "type": "twitter", 35 | "name": "autinerd" 36 | } 37 | }, 38 | "comments": [ 39 | "Liste ist auf Bahn-eigene und nicht komplett offensichtliche Abkürzungen („Ellok“ für „Elektrolokomotive“) begrenzt", 40 | "Groß- und Kleinschreibung ist sehr wichtig. Beispiele: „ZS“: Zugsammelschiene, „Zs“: Zugschaffner.", 41 | "Signaltypen gibt es doppelt: „Asig“ und „ASig“ bzw. „Zvsig“ und „ZVsig“." 42 | ] 43 | }] 44 | } 45 | -------------------------------------------------------------------------------- /data/regeln_vkms.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "regeln_vkm", 3 | "headline": "Fahrzeughalterkennzeichen", 4 | "description": "Alle in der EU registrierten Fahrzeughalter", 5 | "access": [{ 6 | "x_source": "VKM", 7 | "type": "&" 8 | }], 9 | "magic_hashtags": [ 10 | "_EVU", 11 | "_VKM", 12 | "🇪🇺" 13 | ], 14 | "data": [{ 15 | "id": "vkm_liste", 16 | "short": "UNIQUE", 17 | "long": "Keeper Name / Halter Name / Nom du détenteur / Название владельца", 18 | "nolink": true, 19 | "file": "sources/regeln_vkms.csv", 20 | "source": { 21 | "name": "Vehicle Keeper Marking register (VKM)", 22 | "url": "https://www.era.europa.eu/system/files/2023-02/IU-VKM-publiclist-155.xls" 23 | }, 24 | "license": { 25 | "name": "ähnlich CC-BY", 26 | "url": "https://www.era.europa.eu/content/disclaimer-and-copyright-notice", 27 | "owner": { 28 | "type": "name", 29 | "name": "European Union Agency for Railways" 30 | } 31 | } 32 | }] 33 | } 34 | -------------------------------------------------------------------------------- /data/signale_bostrab.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "signale_bostrab", 3 | "headline": "Straßenbahnsignale", 4 | "description": "BOStrab", 5 | "access": [{ 6 | "x_source": "BO", 7 | "type": "%" 8 | }, { 9 | "x_source": "BOSTRAB", 10 | "type": "%" 11 | }], 12 | "magic_hashtags": [ 13 | "BOSTRAB", 14 | "_FFM", 15 | "_HH", 16 | "_HB", 17 | "_STG", 18 | "_KOELN", 19 | "_KÖLN", 20 | "_RNV", 21 | "_DD", 22 | "🍎🚋", 23 | "🚋🍎", 24 | "🚋🍏 ", 25 | "🍏🚋", 26 | "🍔" 27 | ], 28 | "data": [{ 29 | "id": "bostrab", 30 | "short": "Abk", 31 | "long": "Bedeutung", 32 | "file": "sources/signale_bostrab.csv", 33 | "source": { 34 | "name": "Eigene Zusammenstellung" 35 | }, 36 | "license": { 37 | "name": "CC-BY-SA 4.0", 38 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 39 | "owner": { 40 | "type": "twitter", 41 | "name": "moritzkraehe" 42 | } 43 | } 44 | }] 45 | } 46 | -------------------------------------------------------------------------------- /data/signale_de_ds301.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "signale_de_ds301", 3 | "headline": "Eisenbahnsignale Westdeutschland", 4 | "description": "DS301 der Deutschen Bahn", 5 | "access": [{ 6 | "x_source": "DS", 7 | "type": "%" 8 | }], 9 | "magic_hashtags": [ 10 | "DS301", 11 | "DS100", 12 | "_DE", 13 | "_SH", 14 | "_NI", 15 | "_RMV", 16 | "🇩🇪" 17 | ], 18 | "data": [{ 19 | "id": "ds301", 20 | "short": "DSAbk", 21 | "long": "Beschreibung", 22 | "add": "Kurzname", 23 | "delim": ";", 24 | "alias": "|", 25 | "file": "sources/signale_de.csv", 26 | "filter": [{ 27 | "col": "DS/DV", 28 | "contains": "DS", 29 | "empty": true 30 | }], 31 | "source": { 32 | "name": "Eigene Zusammenstellung" 33 | }, 34 | "license": { 35 | "name": "CC-BY-SA 4.0", 36 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 37 | "owner": { 38 | "type": "twitter", 39 | "name": "autinerd" 40 | }, 41 | "contributors": [{ 42 | "type": "twitter", 43 | "name": "baeuchle" 44 | }] 45 | } 46 | }] 47 | } 48 | -------------------------------------------------------------------------------- /data/signale_de_dv301.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "signale_de_dv301", 3 | "headline": "Eisenbahnsignale Ostdeutschland", 4 | "description": "DV301 der Deutschen Bahn", 5 | "access": [{ 6 | "x_source": "DV", 7 | "type": "%" 8 | }], 9 | "magic_hashtags": [ 10 | "DV301", 11 | "_TH" 12 | ], 13 | "data": [{ 14 | "id": "dv301", 15 | "short": "DVAbk", 16 | "long": "Beschreibung", 17 | "add": "Kurzname", 18 | "delim": ";", 19 | "alias": "|", 20 | "file": "sources/signale_de.csv", 21 | "filter": [{ 22 | "col": "DS/DV", 23 | "contains": "DV", 24 | "empty": true 25 | }], 26 | "source": { 27 | "name": "Eigene Zusammenstellung" 28 | }, 29 | "license": { 30 | "name": "CC-BY-SA 4.0", 31 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 32 | "owner": { 33 | "type": "twitter", 34 | "name": "autinerd" 35 | } 36 | }, 37 | "comments": [ 38 | "Signalnamen mit \"/\" können aus technischen Gründen nicht beantwortet werden. Das betrifft Vr1/2." 39 | ] 40 | }] 41 | } 42 | -------------------------------------------------------------------------------- /data/signale_de_gemein.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "signale_de_gemein", 3 | "headline": "Eisenbahnsignale Gemeines Signalbuch", 4 | "description": "Erfundene Signalbegriffe zum Lachen", 5 | "access": [{ 6 | "x_source": "GSB", 7 | "type": "%" 8 | }, { 9 | "x_source": "GEMEIN", 10 | "type": "%" 11 | }], 12 | "magic_hashtags": [ 13 | "_GSB" 14 | ], 15 | "data": [{ 16 | "id": "gemein", 17 | "short": "Abk", 18 | "long": "Kurzname", 19 | "add": "Bedeutung", 20 | "delim": ";", 21 | "file": "sources/signale_de_gemein.csv", 22 | "source": { 23 | "name": "Eigene Zusammenstellung" 24 | }, 25 | "license": { 26 | "name": "CC0", 27 | "url": "https://creativecommons.org/share-your-work/public-domain/cc0", 28 | "owner": { 29 | "type": "twitter", 30 | "name": "9Lukas5" 31 | } 32 | }, 33 | "comments": [ 34 | "Original-Autor des gemeinen Signalbuches unbekannt" 35 | ] 36 | }] 37 | } 38 | -------------------------------------------------------------------------------- /data/signale_no.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "signale_no", 3 | "headline": "Norske signale", 4 | "access": [{ 5 | "x_source": "NO", 6 | "type": "%" 7 | }], 8 | "magic_hashtags": [ 9 | "_NO", 10 | "🇳🇴" 11 | ], 12 | "data": [{ 13 | "id": "signale_no", 14 | "short": "Nummer", 15 | "long": "Betydning", 16 | "alias": ",", 17 | "file": "sources/signale_no.csv", 18 | "source": { 19 | "name": "Trykk 401" 20 | } 21 | }] 22 | } 23 | -------------------------------------------------------------------------------- /data/strecken_ch.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "strecken_ch", 3 | "headline": "Strecken Schweiz", 4 | "access": [{ 5 | "x_source": "CH", 6 | "type": "$" 7 | }], 8 | "magic_hashtags": [ 9 | "_CH", 10 | "🇨🇭" 11 | ], 12 | "data": [{ 13 | "id": "strecken_ch_sbb", 14 | "short": "linie", 15 | "long": "linienname", 16 | "file": "sources/strecken_ch_sbb.csv", 17 | "source": { 18 | "name": "Liniennetz der SBB", 19 | "url": "https://opendata.swiss/de/dataset/linie" 20 | }, 21 | "license": { 22 | "name": "ähnlich CC-BY", 23 | "url": "https://opendata.swiss/de/terms-of-use/#terms_by", 24 | "owner": { 25 | "type": "name", 26 | "name": "SBB" 27 | } 28 | } 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /data/strecken_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "strecken_de", 3 | "description": "Eisenbahnstrecken Deutschland", 4 | "access": [{ 5 | "x_source": "DS", 6 | "type": "$" 7 | }, { 8 | "x_source": "DE", 9 | "type": "$" 10 | }], 11 | "magic_hashtags": [ 12 | "DS100", 13 | "_DE", 14 | "🇩🇪" 15 | ], 16 | "data": [{ 17 | "id": "vzg", 18 | "short": "STRNR", 19 | "long": "STRKURZN", 20 | "add": "STRNAME", 21 | "delim": ";", 22 | "nolink": true, 23 | "file": "sources/strecken_de_vzg.csv", 24 | "description": "Streckennummern nach VzG / Stredax", 25 | "source": { 26 | "name": "Geo-Streckennetz", 27 | "url": "https://data.deutschebahn.com/dataset/geo-strecke", 28 | "modified": true 29 | }, 30 | "license": { 31 | "name": "CC-BY 4.0", 32 | "url": "https://creativecommons.org/licenses/by/4.0/", 33 | "owner": { 34 | "type": "name", 35 | "name": "Deutsche Bahn AG" 36 | } 37 | } 38 | },{ 39 | "id": "kbs", 40 | "short": "Tabellennummer", 41 | "long": "Laufweg", 42 | "add": "Liniennummer", 43 | "alias": ",", 44 | "nolink": true, 45 | "file": "sources/strecken_de_kbs.csv", 46 | "description": "Kursbuchstrecken", 47 | "source": { 48 | "name": "Kursbuch der Deutschen Bahn", 49 | "url": "http://kursbuch.bahn.de/hafas/kbview.exe/dn?rt=1&dosearch=1&searchmode=tableplus&table_nr= &controlpattern=P.ddd&mainframe=utable&tocinfo=reg_tab" 50 | }, 51 | "license": { 52 | "name": " ", 53 | "owner": { 54 | "type": "name", 55 | "name": "Deutsche Bahn AG" 56 | } 57 | }, 58 | "comments": [ 59 | "Dreistellige Streckennummern", 60 | "Unternummern sind auf die erste Zahl reduziert, z.B. $645.3 für die KBS 645.3-4" 61 | ] 62 | },{ 63 | "id": "kbs_alt", 64 | "short": "Tabellennummer", 65 | "long": "Laufweg", 66 | "add": "Liniennummer", 67 | "alias": ",", 68 | "nolink": true, 69 | "file": "sources/strecken_de_alt.csv", 70 | "description": "ehemalige Kursbuchstrecken", 71 | "source": { 72 | "name": "Eigene Zusammenstellung" 73 | }, 74 | "license": { 75 | "name": "CC-BY-SA 4.0", 76 | "url": "https://creativecommons.org/licenses/by-sa/4.0/", 77 | "owner": { 78 | "type": "twitter", 79 | "name": "jonasr_97" 80 | } 81 | } 82 | },{ 83 | "id": "strecken_de_namen", 84 | "short": "Abk", 85 | "long": "Beschreibung", 86 | "add": "Nummer", 87 | "alias": ",", 88 | "nolink": true, 89 | "file": "sources/strecken_de_namen.csv", 90 | "description": "Zusätzliche Streckennamen aus eigener Zusammenstellung", 91 | "source": { 92 | "name": "Eigene Zusammenstellung" 93 | } 94 | }] 95 | } 96 | -------------------------------------------------------------------------------- /data/strecken_ffm.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "strecken_ffm", 3 | "headline": "Stadtbahnstrecken Frankfurt", 4 | "access": [{ 5 | "x_source": "FFM", 6 | "type": "$" 7 | }], 8 | "magic_hashtags": [ 9 | "_FFM", 10 | "🍎🚋", 11 | "🚋🍎", 12 | "🚋🍏 ", 13 | "🍏🚋" 14 | ], 15 | "data": [{ 16 | "id": "strecken_ffm", 17 | "short": "Abk", 18 | "long": "Weg", 19 | "alias": ",", 20 | "file": "sources/strecken_ffm.csv", 21 | "source": { 22 | "name": "Eigene Zusammenstellung aus Krakies/Nagel", 23 | "url": "https://de.wikipedia.org/wiki/Vorlage:Krakies/Nagel", 24 | "modified": true 25 | }, 26 | "comments": [ 27 | "Alle Bauabschnitte der Stadtbahnstrecken können mit großen lateinischen Buchstaben oder den Unicode-Zeichen für römische Zahlen geschrieben werden: ‚$FFM:DIV‘ = ‚$FFM:DⅣ‘ = ‚$FFM:Dⅳ‘." 28 | ] 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /data/strecken_no.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "strecken_no", 3 | "headline": "Strecken Norwegen", 4 | "access": [{ 5 | "x_source": "NO", 6 | "type": "$" 7 | }], 8 | "magic_hashtags": [ 9 | "_NO", 10 | "🇳🇴" 11 | ], 12 | "data": [{ 13 | "id": "strecken_no", 14 | "short": "Blad", 15 | "long": "Vei", 16 | "file": "sources/strecken_no.csv", 17 | "source": { 18 | "name": "Grafische Fahrpläne, Stand 2020", 19 | "url": "https://www.banenor.no/kundeportal/ruter-og-sportilgang/grafiske-togruter1/", 20 | "modified": true 21 | }, 22 | "comments": [ 23 | "Selbst abgetippt" 24 | ] 25 | }] 26 | } 27 | -------------------------------------------------------------------------------- /doc/aufbau-antworten.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Aufbau der Antworten 3 | Wie sind die Antworten des Bots strukturiert und was bedeutet alles? 4 |

5 | 6 | Aufbau der Antworten des Bots 7 | ============================= 8 | 9 | Findet der Bot in einem Status erweiterbare Abkürzungen, so antwortet er 10 | auf diesen Status mit diesen Erweiterungen. Hierbei wird eine Zeile pro 11 | Abkürzung geschrieben: 12 | 13 | FF: Frankfurt (Main) Hbf 14 | 1733: Hannover --Kassel-- - Würzburg 15 | 16 | Ist die Quelle für die Abkürzung nicht DS100 oder BOT, so wird die 17 | Quelle vorangestellt: 18 | 19 | FFM#HB: Frankfurt Hauptbahnhof 20 | FFM$A3: Anschlussstrecke A3: Abzweig Nordwest - Oberursel Hohemark 21 | 22 | Mehrere Kürzel werden in der Reihenfolge beantwortet, in der sie 23 | auftauchen; mehrfach wiederholte gleiche Kürzel werden nur beim ersten 24 | Mal beantwortet. 25 | 26 | Ist die Antwort zu lange für einen Status, antwortet der Bot mit dem 27 | nächsten Teil seiner Antwort auf den vorherigen Teil, sodass ein 28 | Thread seiner Antworten entsteht. 29 | -------------------------------------------------------------------------------- /doc/avatar-bahn.social.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | @ril100@bahn.social 10 | 11 | 14 | 15 | Bjørn Bäuchle <ds100 minus bot at frankfurtium dot de> 16 | https://avatar.frankfurtium.de/ril100@bahn.social.svg 17 | https://avatar.frankfurtium.de/ds100.svg 18 | https://avatar.frankfurtium.de/ds100.svg 19 | © Bjørn Bäuchle CC-0 4.0 20 | image/svg+xml 21 | Image 22 | @ril100@bahn.social 23 | 2022-11-08 24 | 25 | 26 | 27 | 38 | 39 | 40 | 41 | bahn. 42 | social 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /doc/avatar-ril100.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | DS100 10 | 11 | 14 | 15 | Bjørn Bäuchle <ds100 minus bot at frankfurtium dot de> 16 | https://avatar.frankfurtium.de/ril100.svg 17 | https://avatar.frankfurtium.de/ril100.svg 18 | © Bjørn Bäuchle CC-0 4.0 19 | image/svg+xml 20 | Image 21 | Ril100 22 | 2022-03-31 23 | 24 | 25 | 26 | 37 | 38 | 39 | 40 | Ril 100 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /doc/avatar-zug.network.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | @ril100@zug.network 10 | 11 | 14 | 15 | Bjørn Bäuchle <ds100 minus bot at frankfurtium dot de> 16 | https://avatar.frankfurtium.de/ril100@zug.network.svg 17 | https://avatar.frankfurtium.de/ds100.svg 18 | https://avatar.frankfurtium.de/ds100.svg 19 | © Bjørn Bäuchle CC-0 4.0 20 | image/svg+xml 21 | Image 22 | @ril100@zug.network 23 | 2022-11-08 24 | 25 | 26 | 27 | 38 | 39 | 40 | 41 | zug. 42 | network 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /doc/avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | DS100 10 | 11 | 14 | 15 | Bjørn Bäuchle <ds100 minus bot at frankfurtium dot de> 16 | https://avatar.frankfurtium.de/ds100.svg 17 | https://avatar.frankfurtium.de/ds100.svg 18 | © Bjørn Bäuchle CC-0 4.0 19 | image/svg+xml 20 | Image 21 | DS100 22 | 2019-06-20 23 | 24 | 25 | 26 | 37 | 38 | 39 | 40 | DS 100 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /doc/bot.css: -------------------------------------------------------------------------------- 1 | h1::before { 2 | content: ""; 3 | display: inline-block; 4 | background-image: url(https://avatar.frankfurtium.de/ds100.svg); 5 | background-size: 100% 100%; 6 | height: 25px; 7 | width: 25px; 8 | } 9 | 10 | blockquote { 11 | border: thin blue solid; 12 | font-family: sans-serif; 13 | padding: .5em; 14 | border-radius: 1em; 15 | } 16 | 17 | blockquote.antwort { 18 | margin-left: 6em; 19 | } 20 | 21 | blockquote.antwort2 { 22 | margin-left: 9em; 23 | } 24 | 25 | blockquote.antwort3 { 26 | margin-left: 12em; 27 | } 28 | 29 | blockquote::before { 30 | display: block; 31 | margin-bottom: .5em; 32 | color: #888; 33 | } 34 | 35 | table, th, td { 36 | border-collapse: collapse; 37 | border: thin black solid; 38 | } 39 | 40 | table tbody th { 41 | text-align: left; 42 | } 43 | 44 | table tbody td { 45 | text-align: center; 46 | } 47 | 48 | table.dumptable tbody td { 49 | text-align: left; 50 | } 51 | 52 | table.dumptable tbody th { 53 | text-align: left; 54 | font-family: monospace; 55 | } 56 | 57 | th.yes, td.yes { 58 | background-color: #00ff00; 59 | } 60 | 61 | th.note, td.note { 62 | background-color: #ffd000; 63 | } 64 | 65 | th.no, td.no { 66 | background-color: #ff8080; 67 | } 68 | 69 | header, main, section { 70 | clear: both; 71 | } 72 | 73 | navi p { 74 | display: block; 75 | float: left; 76 | margin-right: 1em; 77 | margin-top: .1em; 78 | margin-bottom: .3em; 79 | } 80 | 81 | navi p:before { 82 | content: "☰ "; 83 | display: inline-block; 84 | margin-right: .2em; 85 | } 86 | 87 | .closenavi:before { 88 | content: "× "; 89 | display: inline-block; 90 | font-size: 200%; 91 | font-weight: bold; 92 | } 93 | 94 | div.shownnavi { 95 | width: 250px; 96 | } 97 | 98 | div.dumpnavi.shownnavi { 99 | max-width: 100%; 100 | min-width: 400px; 101 | } 102 | 103 | div.hiddennavi { 104 | width: 0px; 105 | } 106 | 107 | div.sidenavi { 108 | height: 100%; 109 | position: fixed; 110 | z-index: 1; 111 | top: 0; 112 | left: 0; 113 | transition: 0.5s; 114 | overflow-x: hidden; 115 | background-color: #c0d0ff; 116 | } 117 | 118 | div.dumpnavi { 119 | height: 100%; 120 | position: fixed; 121 | z-index: 1; 122 | top: 0; 123 | right: 0; 124 | transition: 1s; 125 | overflow-x: hidden; 126 | overflow-y: scroll; 127 | background-color: #c0d0ff; 128 | } 129 | 130 | navi ul { 131 | list-style-type: none; 132 | margin-left: 0; 133 | padding: 0; 134 | } 135 | 136 | navi ul li.hiddennavi div:before { 137 | content: "▷ "; 138 | } 139 | 140 | navi ul li.shownnavi div:before { 141 | content: "▽ "; 142 | } 143 | 144 | navi ul li { 145 | display: block; 146 | padding: .2em; 147 | background-color: #c0d0ff; 148 | color: #202840; 149 | } 150 | 151 | navi ul li:hover { 152 | background-color: #80a0ff; 153 | color: white; 154 | } 155 | 156 | navi ul li ul { 157 | transition: 0.5s; 158 | margin-left: 1em; 159 | } 160 | 161 | navi li a { 162 | display: block; 163 | color: white; 164 | text-decoration: none; 165 | color: inherit; 166 | width: 100%; 167 | } 168 | 169 | navi li > a { 170 | color: black; 171 | } 172 | 173 | navi li:hover > a { 174 | color: white; 175 | } 176 | 177 | navi ul li.transform_version:hover { 178 | background-color: #c0d0ff; 179 | color: black; 180 | } 181 | 182 | navi ul li.hiddennavi ul { 183 | display: none; 184 | } 185 | 186 | navi ul li.shownnavi ul { 187 | display: block; 188 | } 189 | 190 | div span.sigil { 191 | padding-right: .5em; 192 | font-weight: bold; 193 | font-family: monospace; 194 | } 195 | 196 | div.bigdumplist div.closenavi { 197 | display: none; 198 | } 199 | 200 | .only-mastodon { 201 | display: initial; 202 | color: #ff0000; 203 | } 204 | 205 | .only-mastodon:before { 206 | content: ""; 207 | display: inline-block; 208 | background-image: url('mastodon-icon.png'); 209 | background-size: 100% 100%; 210 | height: 16px; 211 | width: 16px; 212 | margin-right: 4px; 213 | } 214 | -------------------------------------------------------------------------------- /doc/contribute.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Möglichkeiten, beizutragen 3 | Wie kann man beitragen, um den Bot zu verbessern oder ihm neues beizubringen? 4 |

5 | 6 | Beitragen 7 | ========= 8 | 9 | Kommentare zum Bot, Fehlermeldungen und Ideen können gerne per Mastodon an 10 | den [Bot selbst](https://zug.network/@ril100) oder (besser) an 11 | [@baeuchle@chaos.social](https://chaos.social/@baeuchle) geschickt 12 | werden oder als Issue bei 13 | [github](https://github.com/baeuchle/ds100bot/) angelegt werden. 14 | 15 | Der Bot [beinhaltet](/copyright.html) Bahnabkürzungslisten von anderen 16 | Bahnverwaltungen und kann sehr gerne um noch andere Listen erweitert 17 | werden. Wer eine solche Liste hat und weitergeben darf, kann sich gerne 18 | melden, siehe vorheriger Absatz. 19 | 20 | Neue Daten sollten nach Möglichkeit mit [Datenliste im 21 | CSV-Format](/sources.html) kommen, sowie nach Möglichkeit mit einer 22 | [Konfigurationsdatei im JSON-Format](/data.html) (Details jeweils hinter 23 | dem Link.) 24 | 25 | Bedingungen zum Beitragen 26 | ------------------------- 27 | 28 | Beiträge zum Code stehen unter [der gleichen Lizenz](/copyright.html) 29 | wie der restliche Code; die Autorenschaft ist bei github in der Commit 30 | history ersichtlich. 31 | 32 | Datenlisten können unter davon abweichenden Lizenzen stehen. Wichtig 33 | ist: [Die Betreibenden des Bots](/copyright.html) erhalten 34 | mindestens das uneingeschränkte Recht, die Datenliste als Dump zu 35 | veröffentlichen, anzupassen und die Datenliste im git-Repository zu 36 | archivieren. Die Erstellerin der Datenliste hat das Recht, ohne Angabe 37 | von Gründen die weitere Veröffentlichung zu untersagen, aber nicht, dass 38 | die git-Historie verändert wird, um die Daten nachträglich zu löschen. 39 | 40 | Einem Löschen der Datenliste nach Anforderung durch ihre Erstellerin 41 | kann eine direkte Wiederveröffentlichung durch die Betreibenden des Bots 42 | folgen, sofern die ursprüngliche Lizenz dies erlaubt. (Also etwa bei den 43 | CC-Lizenzen). 44 | 45 | Einzelne Einträge in den Datenlisten dürfen unter keinen Umständen Texte 46 | enthalten, auf die jemand urheberrechtliche Ansprüche stellt oder 47 | stellen kann. 48 | -------------------------------------------------------------------------------- /doc/copyright.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Verwendete Daten und Lizenzen 3 | Welche Daten verwendet der Bot und welche rechtlichen Bedingungen gelten dafür? 4 |

5 | 6 | Daten und Urheberrecht 7 | ====================== 8 | 9 | Output des Bots 10 | --------------- 11 | 12 | Die Statusmeldungen, die vom Bot erzeugt werden, besitzen keine 13 | ausreichende Schöpfungstiefe, die die Anwendung eines Urheberrechtes 14 | rechtfertigen würden. 15 | 16 | Datensammlungen 17 | --------------- 18 | 19 | Die vom Bot verwendeten [Datentabellen](/dumps/) besitzen u.U. eine 20 | genügend hohe Schöpfungstiefe. Soweit nicht anders angegeben, stehen 21 | diese Datensammlungen unter © [Apache Software Lizenz 22 | 2.0](http://www.apache.org/licenses/LICENSE-2.0) [Bjørn 23 | Bäuchle](https://chaos.social/@baeuchle). 24 | 25 | Dokumentation 26 | ------------- 27 | 28 | Die vorliegende Sammlung von Dokumentationsseiten unterliegen © [Apache 29 | Software Lizenz 2.0](http://www.apache.org/licenses/LICENSE-2.0) [Bjørn 30 | Bäuchle](https://chaos.social/@baeuchle). 31 | 32 | Verwendete Daten 33 | ---------------- 34 | 35 | Der Bot greift auf verschiedene Datenquellen zurück. Die Auswahl der 36 | Datenquelle geschieht auf Grund des ersten Teils des Suchbegriffs, etwa 37 | bei ‚\#DS:FF‘ der Teil ‚__\#DS:__‘. Ist kein solcher Teil vorhanden 38 | (‚FF‘) oder besteht er nur aus ‚__\#__‘ oder ‚__$__‘ (‚\#FF‘), werden 39 | die Quellen aus dem aktuellen __[Magic Hashtag](finde-listen.html)__ 40 | oder __\#BOT__ (nur bei ‚__\#__‘) benutzt. 41 | 42 | Folgende Datenquellen werden vom Bot benutzt; Informationen zu den 43 | jeweiligen Lizenzen und Quellen finden sich bei den Dumps: 44 | -------------------------------------------------------------------------------- /doc/datenschutz.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Datenschutz 3 | Es werden keine personalisierten Daten erhoben. 4 |

5 | 6 | Datenschutz 7 | =========== 8 | 9 | Der Bot erhebt oder speichert keine personalisierten Daten. Die Antworten 10 | des Bots sind nur auf dem jeweiligen Sozialen Netzwerk abrufbar; für die 11 | Daten, die dieses Netzwerk erhebt, besteht von Seiten des Bots keine 12 | Verantwortung. 13 | 14 | Der Bot speichert die letzte Status-ID, die er gesehen hat, damit er 15 | nicht zweimal auf den gleichen Status antwortet, sowie jede Abkürzung, 16 | die er in einem Status zu erkennen glaubte. Diese werden zusammen mit 17 | dem Erkennungsdatum und der Information, ob die Abkürzung einem 18 | Langnamen zugeordnet werden konnte, gespeichert. Es wird dabei nicht 19 | gespeichert, welcher User den Ursprungsstatus verfasst oder anderweitig 20 | zur Aufmerksamkeit des Bots gebracht hat (etwa durch Boost). Aus dieser 21 | Liste kann in regelmäßigen oder unregelmäßigen Abständen eine 22 | Zusammenstellung der am häufigsten gesuchten Abkürzungen und der 23 | nicht-erkannten Abkürzungen erstellt werden. Durch Löschen der 24 | entsprechenden Toots bei Mastodon sind diese Daten auch über den Bot 25 | oder dessen gespeicherte Daten nicht mehr zugänglich. Statistiken 26 | werden davon nicht beeinflusst, und die Details der Löschung obliegt dem 27 | jeweiligen Sozialen Netzwerk. 28 | 29 | Die Statistik wird an jedem ersten eines Monats für den letzten Monat, 30 | an jedem 1. Januar für das letzte Jahr und immer am 9. Juni (dem 31 | Geburtstag des Bots) für die gesamte Lebensdauer des Bots 32 | veröffentlicht. 33 | 34 | Beim Besuch *dieser* Seite werden im Rahmen der gesetzlichen 35 | Vorschriften keine personalisierten Daten erhoben. Es werden keine Cookies 36 | gesetzt und kein Inhalt von externen Servern, die nicht unter der gleichen 37 | Kontrolle wie dieser Server liegen, geladen. 38 | -------------------------------------------------------------------------------- /doc/faq.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: FAQ 3 | Sammlung von häufig gestellten Fragen 4 |

5 | 6 | Häufig gestellte Fragen 7 | ======================= 8 | 9 | (oder was ich dafür halte) 10 | 11 | Siehe auch die [FAQ-Abkürzungsliste des Bots](/dumps/faq.html). 12 | 13 | Mach doch mal ein einfaches Beispiel! 14 | ------------------------------------- 15 | 16 | - So sieht der Bot einen Status, und wahrscheinlich auch Antworten 17 | darauf: 18 | 19 | > @ril100@zug.network \#FF 20 | 21 | Warum antwortet mir der Bot nicht, obwohl er mir folgt? 22 | ------------------------------------------------------- 23 | 24 | - Mastodon braucht manchmal ein bisschen Zeit, um Toots an die richtige 25 | Stelle zu liefern. Das ist systemimmanent und kann nicht verbessert 26 | werden. 27 | - Vielleicht ist er grade kaputt, der Rechner, auf dem er läuft 28 | überlastet oder was anderes läuft schief. 29 | 30 | Warum antwortet der Bot mir, obwohl ich das nicht will? 31 | ------------------------------------------------------- 32 | 33 | - Der Bot antwortet Benutzerinnen, denen er folgt, automatisch, aber er 34 | folgt nicht automatisch. Siehe unten. 35 | - Bei Status von nicht-gefolgten Benutzerinnen reagiert er auf 36 | Erwähnungen und seine [Magic Hashtags](/finde-listen.html) – aber nur 37 | dann, wenn auch eine erweiterbare Abkürzung im Toot steckt. Wenn das 38 | nicht erwünscht ist, ist der Bot nicht beleidigt, wenn er geblockt 39 | wird. Da der Bot keine personalisierten Daten speichert, kann man ihm 40 | nicht anders sagen, dass man von ihm ignoriert werden will. Wem es 41 | egal ist, ob der Bot antwortet oder nicht, aber selbst die Antworten 42 | des Bots einfach nicht sehen will, kann den Bot auf Stumm schalten. 43 | - Soll der Bot nur auf einzelne Toots nicht antworten (aber das muss 44 | man vorher wissen), kann dieser Toot mit #NOBOT markiert werden. 45 | - Der Bot antwortet auch, wenn jemand anders einen Toot beantwortet und 46 | in dieser Antwort den Bot markiert oder einen der Magic Hashtags 47 | benutzt. Das kann für die Verfasserin des originalen Toots verwirrend 48 | sein. 49 | 50 | Warum ist Groß-/Kleinschreibung so wichtig? 51 | ------------------------------------------- 52 | 53 | Ein Grundprinzip bei der Entwicklung des Bots ist „Don’t Spam”. Dazu 54 | zählt, dass es möglichst wenige „Kollateralfunde“ geben sollte – Dinge, 55 | die gar nicht beantwortet werden sollten. Je genauer ein bestimmtes 56 | Kürzel angegeben werden muss, desto kleiner die Wahrscheinlichkeit, dass 57 | es gar nicht gemeint war. 58 | -------------------------------------------------------------------------------- /doc/finde-toots.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Finden von Toots 3 | Welche Toots betrachtet der Bot, um sie zu beantworten? 4 |

5 | 6 | Finden von Toots 7 | ================ 8 | 9 | Der Bot durchsucht Mastodon nach 10 | 11 | - Toots in seiner Timeline, also von Benutzenden, denen der Bot folgt 12 | (siehe [Interaktion](/interaktion.html)). Antworten auf nicht-Gefolgte 13 | werden meistens nicht gefunden. 14 | - Toots, die den Bot erwähnen: 15 | - Explizite Erwähnungen (d.h., @ril100@zug.network steht im 16 | sichtbaren Text) 17 | - Implizite Erwähnungen (z.B. Antworten auf den Bot oder auf Toots, 18 | die den Bot erwähnen) 19 | - [Magic Hashtags](/finde-listen.html) können **nicht** dafür benutzt 20 | werden, Toots zu finden. 21 | 22 | Der Bot schließt aus: 23 | 24 | - Pure Boosts 25 | - Seine eigenen Toots 26 | 27 | Wird in einem Toot **A** ein anderer Toot **B** beantwortet oder 28 | zitiert, so wird Toot **B** ebenfalls betrachtet. Die 29 | Ausschlusskriterien treffen auch hier zu. 30 | 31 | Sind die Toots gefunden, wird als nächstes analysiert, [welche 32 | Abkürzungslisten benutzt werden sollen](/finde-listen.html) 33 | -------------------------------------------------------------------------------- /doc/haftung.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Haftung 3 | Es wird keinerlei Haftung übernommen. 4 |

5 | 6 | Haftungsausschluss 7 | ================== 8 | 9 | Die Daten, die dieser Bot liefert, sollten richtig und vollständig sein, 10 | aber dafür wird keine Garantie gegeben. 11 | 12 | Schäden, die dadurch entstehen, dass dieser Bot fehlerhafte oder 13 | unvollständige Informationen liefert, liegen einzig und alleine in der 14 | Verantwortung derjenigen Person, die diese Informationen 15 | weiterverwendet. 16 | 17 | Insbesondere behält sich der Autor vor, einzelne Einträge zu löschen, 18 | nicht-offizielle Einträge hinzuzufügen und / oder Einträge zu 19 | verfälschen, und all dies __ohne gesonderte Ankündigung__. Besondere 20 | Vorsicht sei geboten bei #FO, was eventuell irgendwann nicht mehr "Stadt 21 | mit schlechtesten Autofahrern der Republik Hbf" heißen könnte. 22 | -------------------------------------------------------------------------------- /doc/ignorelist.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Ignorierliste 3 | Was ist die Ignorierliste des Bots und warum gibt es sie? 4 |

5 | 6 | Ignorierliste 7 | ============= 8 | 9 | Einige valide DS100-Kürzel werden auf Sozialen Medien oder im allgemeinen 10 | Sprachgebrauch ebenfalls benutzt und werden daher vom Bot nicht 11 | standardmäßig übersetzt. Beispiele sind Eisenbahnbegriffe wie __\#LZB__, 12 | __\#SBB__, aber auch profane Dinge wie __\#WLAN__ oder __\#ARD__. Bei 13 | [den anderen Dumps](/dumps/) gibt es eine vollständige [Liste der 14 | aktuellen Ignorierliste](/dumps/blacklist.html). 15 | 16 | Die Einträge auf der Ignoriertlist werden nicht gefunden, wenn nicht die 17 | Quelle angegeben ist: __\#WLAN__ wird nicht beantwortet, __\#DS:WLAN__ 18 | schon. 19 | 20 | Es gibt bisher keine strukturierte Art und Weise, wie neue Begriffe auf 21 | die Ignorierliste kommen können, und keine Regeln, nach denen neue 22 | Begriffe ausgewählt werden, außer das Gutdünken des Autors. 23 | -------------------------------------------------------------------------------- /doc/impressum.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Impressum 3 | Wer steckt hinter dem Bot? 4 |

5 | 6 | Impressum 7 | ========= 8 | 9 | Die meisten Toots des Bots werden automatisch nach den [dargestellten 10 | Regeln](/finde-lang.html) erzeugt. Erklärungen und Hilfestellungen 11 | können auch von 12 | [@baeuchle@chaos.social](https://chaos.social/@baeuchle/) gegeben werden 13 | und sollten einfach erkennbar sein. 14 | 15 | Angaben gemäß §5 TMG: 16 | --------------------- 17 | 18 | Verantwortlich für diesen Bot und diese Seite ist:
19 | Dr. Bjørn Bäuchle
20 | Am Garten 1
21 | 34121 Kassel
22 | ds100 minus bot at frankfurtium dot de 23 | 24 | Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV: 25 | ---------------------------------------------------- 26 | 27 | Bjørn Bäuchle 28 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 |

2 | DS100-Bot Startseite 3 | Erklärungen und Dokumentation zum Eisenbahnabkürzungsbot für 4 | Social Media 5 |

6 | 7 | DS100/Ril100-Bot 8 | ================ 9 | 10 | Diese Seite beschreibt den Eisenbahnabkürzungsbot für 11 | Mastodon (@ril100@zug.network und @ril100@bahn.social). 14 | 15 | Zweck des Bots 16 | -------------- 17 | 18 | Der Bot soll helfen, Betriebsstellen Deutschen Bahn nach Ril 100 19 | (Betriebsstellenverzeichnis, ehemals DS 100) abzukürzen und trotzdem 20 | sicherzustellen, dass auch Dritte den Inhalt des Toots verstehen 21 | können. 22 | 23 | Der Bot entspringt aus einem privaten Wochenendprojekt und wird _pro 24 | bono_ betrieben; es stehen keine wirtschaftlichen Interessen und keine 25 | Umsatz- oder Gewinnabsicht hinter Erstellung und / oder Betreiben des Bots. 26 | 27 | Twitter vs. Mastodon 28 | -------------------- 29 | 30 | Dieser Bot wurde ursprünglich für Twitter geschrieben und später so erweitert, 31 | dass er auch bei Mastodon eingesetzt werden kann. Da aus Twitter 32 | mittlerweile ein faschistisches Arschlochloch geworden ist, wird dieses 33 | Netzwerk nicht mehr unterstützt. 34 | 35 | Instanz-spezifische Mastodon-Accounts 36 | ===================================== 37 | 38 | Der Account @ril100@bahn.social beantwortet 40 | Toots in der lokalen Timeline der jeweiligen Bahn-zentrierten 41 | Mastodon-Instanzen. 42 | 43 | Kurze Funktionsübersicht: Vier Schritte 44 | --------------------------------------- 45 | 46 | Der Bot funktioniert in vier Schritten: 47 | 48 | 1. [Suche nach Toots](/finde-toots.html), die eventuell beantwortet 49 | werden könnten 50 | 2. [Herausfinden](/finde-listen.html), welche 51 | [Abkürzungslisten](/copyright.html) benutzt werden sollen 52 | 3. Suche nach [Abkürzungen](/finde-lang.html) in Spoilertext, 53 | Toot-Inhalt und Bild-Alternativtexten 54 | 4. [Antwort auf die Toots](/aufbau-antworten.html) erstellen, in dem die 55 | Abkürzungen ausgeschrieben werden. 56 | 57 | Mit dem Bot kann auch [interagiert](/interaktion.html) werden. 58 | -------------------------------------------------------------------------------- /doc/interaktion.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Interaktion 3 | Wie kann man mit dem Bot interagieren? 4 |

5 | 6 | Interaktion mit dem Bot 7 | ======================= 8 | 9 | Der Bot kann Benutzerinnen automatisch folgen, um ihre Toots in seiner 10 | Timeline zu sehen und damit ohne gesonderte Aufwände auf diese Toots zu 11 | antworten. 12 | 13 | Hierfür muss der Bot explizit erwähnt werden (eine einfache Antwort an 14 | den Bot reicht also nicht: der sichtbare Text des Toots muss 15 | „__@\_ds\_100__“ enthalten!) und der Hashtag __\#folgenbitte__ erwähnt 16 | werden. (Die Kleinschreibung bei diesem Hashtag ist wichtig!) 17 | 18 | Ähnlich kann der Bot auch dazu gebracht werden, automatisch wieder zu 19 | entfolgen. Hierfür muss der Hashtag __\#entfolgen__ (wieder: klein 20 | geschrieben!) mit einer expliziten Erwähnung verbunden werden. 21 | 22 | Zu beiden Aktionen gibt der Bot keine eigene Rückmeldung; ob der Vorgang 23 | erfolgreich war, muss selbsttätig über die eigenen Social Media-Kanäle 24 | herausgefunden werden. 25 | 26 | Der Bot gibt auch darüber Auskunft, welchen Nutzer\*innen-spezifischen 27 | Magic Hashtag er standardmäßig verwendet; dafür muss er mit dem Hashtag 28 | __\#showdefault__ zusammen (explizit) erwähnt werden. 29 | -------------------------------------------------------------------------------- /doc/links.snip: -------------------------------------------------------------------------------- 1 |
2 |
Navigation schließen
3 | 44 |
45 | -------------------------------------------------------------------------------- /doc/mastodon-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baeuchle/ds100bot/1a76be55a87de7793e37ea25d248d1d904843e11/doc/mastodon-icon.png -------------------------------------------------------------------------------- /doc/motivation.md: -------------------------------------------------------------------------------- 1 |

2 | DS-100: Motivation 3 | Warum gibt es diesen Bot? 4 |

5 | 6 | Motivation 7 | ========== 8 | 9 | Dieser Bot wurde motiviert durch die Unfähigkeit des Social Media Teams eines 10 | großen Deutschen Eisenbahnunternehmens, mit Menschen zu kommunizieren, die 11 | effizienterweise gerne DS100-Kürzel einsetzen wollten, um Betriebsstellen nicht 12 | ausschreiben zu müssen und trotzdem eindeutig benennen zu können. 13 | 14 | Den Ärger darüber, dass nicht nur Unverständnis geäußert wurde, sondern sogar 15 | Menschen dafür getadelt wurden, diese Kürzel zu benutzen („es könnten ja auch 16 | Menschen mitlesen, die das dann nicht verstehen“), leitete der Autor des Bots 17 | in dessen Erstellung um und machte somit etwas Konstruktives, anstatt sich 18 | weiter zu ärgern. 19 | -------------------------------------------------------------------------------- /doc/script.js: -------------------------------------------------------------------------------- 1 | function toggle_show(element) { 2 | var calculated_class_name = "only-" + element.value; 3 | var calculated_display = element.checked ? "initial" : "none"; 4 | console.log(calculated_class_name + ": " + calculated_display); 5 | all_elements = document.getElementsByClassName(calculated_class_name); 6 | for (var i = 0; i < all_elements.length; ++i) { 7 | all_elements[i].style.display = calculated_display; 8 | } 9 | } 10 | 11 | function toggle_menu() { 12 | toggle_any("sidenavi"); 13 | } 14 | 15 | function toggle_any(classname) { 16 | var elements = document.getElementsByClassName(classname); 17 | for (var i = 0; i < elements.length; ++i) { 18 | elements[i].classList.toggle("shownnavi"); 19 | elements[i].classList.toggle("hiddennavi"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /doc/twitter-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baeuchle/ds100bot/1a76be55a87de7793e37ea25d248d1d904843e11/doc/twitter-icon.png -------------------------------------------------------------------------------- /ds100bot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """Twitter-Bot für die Expansion Abkürzungen""" 4 | 5 | import argparse 6 | import configparser 7 | import logging 8 | 9 | from AnswerMachine import handle_list 10 | from Externals import setup_database, set_arguments, setup_network, test_network_arguments 11 | import Persistence 12 | 13 | logger = Persistence.init_logger() 14 | 15 | if __name__ == "__main__": 16 | parser = argparse.ArgumentParser(description=__doc__) 17 | Persistence.set_logging_args(parser) 18 | set_arguments(parser) 19 | parser.add_argument('--no-version', 20 | dest='notify_version', 21 | help='Do not send out version status and do not store last version', 22 | required=False, 23 | action='store_false', 24 | default=True) 25 | args = parser.parse_args() 26 | configuration = configparser.ConfigParser() 27 | configuration.read(args.config) 28 | args.config = configuration 29 | network_name = test_network_arguments(args) 30 | logger.setLevel(getattr(logging, args.log_level)) 31 | logger.debug("%s bot running args: %s", network_name, args) 32 | logging.getLogger('msg').setLevel(getattr(logging, args.log_level)) 33 | try: 34 | database = setup_database(args, network_name) 35 | network = setup_network(network_name, args, Persistence.get_since_id(database)) 36 | if args.notify_version: 37 | Persistence.notify_new_version(network, database) 38 | 39 | magic_tags, magic_emojis = database.magic_hashtags() 40 | handle_list(network=network, 41 | database=database, 42 | magic_tags=magic_tags, 43 | magic_emojis=magic_emojis) 44 | if args.notify_version: 45 | Persistence.store_version(database) 46 | Persistence.store_since_id(database, network) 47 | database.close_sucessfully() 48 | except BaseException as be: 49 | logger.exception("Failure for network %s", network_name) 50 | raise SystemExit(1) from be 51 | logger.info("Bot finished") 52 | -------------------------------------------------------------------------------- /sources/README.md: -------------------------------------------------------------------------------- 1 | Datenlisten 2 | =========== 3 | 4 | Das Quelltextverzeichnis sources/ enthält die Datenlisten, die für den 5 | Bot importiert werden. Diese Datenlisten werden in den Konfigurationen 6 | in data/ referenziert. 7 | 8 | Die Listen sind im CSV-Format; die erste Zeile enthält die Spaltennamen, 9 | die in der Konfiguration (siehe Verzeichnis data/) den Datenbank-Spalten 10 | zugeordnet werden. 11 | 12 | Die Dateien sind in UTF-8 codiert und haben keine Byte-Order Mark. 13 | 14 | Jeder einzelne Datensatz sollte als Antwort in einen Tweet passen, das 15 | heißt für eine Datenquelle mit Default-Quellenangabe 'XS' gilt für einen 16 | Datensatz mit Abkürzung ABK, dass der Langname 272 Zeichen lang sein 17 | darf, weil ``XS#ABK: `` eben 8 Zeichen lang ist: 18 | ``XS#ABK: _HIER_PASSEN_NOCH_272_ZEICHEN_REIN_``. 19 | 20 | Vorsicht jedoch: Twitter zählt manche Zeichen doppelt. Das betrifft 21 | allerdings 'nur' sehr ausgefallene Satzzeichen und nicht-europäische 22 | Alphabete. [Siehe hier für 23 | Details](https://developer.twitter.com/en/docs/basics/counting-characters). 24 | -------------------------------------------------------------------------------- /sources/faq.csv: -------------------------------------------------------------------------------- 1 | Abk;Name 2 | LEERZEICHEN;Leerzeichen in offiziellen Kürzeln müssen durch _ ersetzt werden. Abkürzungen mit Leerzeichen zu erkennen, widerspricht #NOSPAM. Leerzeichen weglassen wird uneindeutig: https://ds100.frankfurtium.de/leerzeichen_ds100.html 3 | GROSSKLEIN;Groß- und Kleinbuchstaben müssen wie in der jeweiligen Quelle benutzt werden, z.B. ALLES GROß für die DS100. 4 | SIGNAL;Signallisten werden mit % aktiviert. Zum Merken denke man an ein Form-%Vr2, das so ein bisschen wie ein % aussieht. 5 | STRECKE;Streckenlisten werden mit $ aktiviert, z.B. $1733 6 | LINIE;Linienlisten werden mit / aktiviert, z.B. /ICE20 7 | MAGICCHAR;Je nach Art der Abkürzungslisten werden unterschiedliche Anfangszeichen benutzt: # für Orte, $ für Strecken, % für Signale, / für Linien, & für andere Abkürzungen. 8 | STELLWERK;Auf Stellwerken stehen oft Abkürzungen. Diese sind meistens aus Anfangsbuchstaben des Ortes + "f" oder "w" gebildet: *F*ahrdienstleiterin und *W*ärterin. Die Abkürzungen sind höchstens in der direkten Umgebung eindeutig und nicht im Bot hinterlegt. 9 | WIEDERHOLUNG;Der Bot beantwortet jeden Tweet, in dem etwas Beantwortbares ist, auch wenn er den Tweet davor auch schon mit dem gleichen Kürzel beantwortet hat. Threads erkennen und auf Wiederholungen nicht mehr zu reagieren, ist sehr aufwändig, daher bleibt das vorerst so. 10 | REPLY;Der Bot findet ungekennzeichnete Tweets von Accounts, denen er folgt, nicht, wenn diese Tweets auf jemanden Antworten, dem der Bot *nicht* folgt, genauso, wie man solche Tweets in seiner eigenen Timeline auch nicht sieht. Mit Markierung (@_ds_100 oder #DS100) klappt's. 11 | USAGE;Expansion von DS100- und anderen Kürzeln. https://ds100.frankfurtium.de/\n#DS100 im Tweet und Kürzel als Hashtag markieren, bspw #FF\nDetails https://ds100.frankfurtium.de/finde-lang.html 12 | BILDERKENNUNG;Der Bot kann keine Bilderkennung. Wenn er manchmal zu wissen scheint, was auf einem Bild zu sehen ist, weiß er stattdessen, was im Alt-Text zu dem Bild steht. Siehe auch #KI. 13 | DARFICH;Ja, du darfst diesen Bot benutzen, wenn du kein Nazi bist. Siehe #TERMS. #FCKNZS #NAZISRAUS 14 | QUELLENWAHL;Der Bot findet alle Tweets mit Magic Hashtag [MHT] (z.B. #_CH für 🇨🇭, dann wird aus #BN Bern). Mit anderem MHT, z.B. #DS100/Erwähnung, @_ds_100/Antwort, in der der Bot erwähnt wird, kann man #CH:BN benutzen. Siehe auch #FAQ:USERDMT 15 | STANDARDQUELLE;Bei Folgenlassen/Erwähnen interpretiert der Bot Hashtags laut Profil (siehe #FAQ:USERDMT) oder wie #HE als 🇩🇪 (= DS100-Abkürzung). Für andere Quelle (Bsp: DIDOK aus 🇨🇭): Magic Hashtag benutzen (#_CH #HE) oder Quellenkürzel dazu (#CH:HE) 16 | WARTEHALT;Wenn ich kurz gelaufen bin, muss ich mich verschnaufen. Das dauert immer exakt 3 Minuten. Schneller Antworten ist also nicht, warte halt mal ein bisschen. 17 | WASISTDS100;Kürzel der DS100 bestehen aus 1 Buchstabe f Region (hAmbg, Berlin, Dresden, Essen, Ffm, Hann, Köln, haLle, München, Nürnbg, kaRlsr, Saarbrck, sTuttgart, erfUrt, schWerin. I,Q,V,Y s. #FAQ:SONDERGRUPPEN, X,Z s. #FAQ:AUSLAND) und 1-4 Buchstaben für Betriebsstelle. 18 | SONDERGRUPPEN;Kürzel der DS100 mit I,Q,V und Y sind Sondergruppen (siehe #FAQ:WASISTDS100): I=16,7-Hz-Strominfrastruktur, Q=50Hz-Infrastruktur, V=Tankanlagen, Y=Grenzpunkte (Streckenwechsel / Regionalbereiche) 19 | AUSLAND;Kürzel mit X und Z haben als zweiten Buchstaben das Land: XA🇦🇹 XB🇧🇪 XC🇷🇺 XD🇩🇰 XE🇪🇸 XF🇫🇷 XG🇬🇷 XH🇫🇮 XI🇮🇹 XJ🇷🇸 XK🇬🇧 XL🇱🇺 XM🇭🇺 XN🇳🇱 XO🇳🇴 XP🇵🇱 XQ🇹🇷 XR🇭🇷 XS🇨🇭 XT🇨🇿 XU🇷🇴 XV🇸🇪 XW🇧🇬 XX🇵🇹 XY🇸🇰 XZ🇸🇮 ZA🇲🇰 ZB🇧🇦 ZE🇪🇪 ZI🇮🇪 ZK🇰🇿 ZL🇱🇹 ZM🇲🇩 ZT🇱🇻 ZU🇺🇦 ZW🇧🇾 20 | ZITAT;Wird der Bot in Antworten oder Zitaten erwähnt oder ist dort ein Magic Hashtag, guckt er auch den zitierten/beantworteten Status an (nur einen, nicht alle im Thread) 21 | USERDMT;Nutzer*innen-spezifische Standardquellen können z.B. durch "mht: #_CH" im Profiltext eingestellt werden, siehe auch https://ds100.frankfurtium.de/finde-listen.html. Der Bot sagt, was er tut, bei "@_ds_100 #showdefault". 22 | -------------------------------------------------------------------------------- /sources/linien_bhvbus.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 501;Wulsdorf Bahnhofstr. - Grünhöfe - Hauptbahnhof - Elbinger Platz - Weserfähre - Stadtverwaltung - Parkstr. - Leherheide West (Schnellbus) 3 | 502;Leherheide West - Parkstr. - Flötenkiel - Lehe - Havenwelten - Hauptbahnhof - Humboldtschule - Grünhöfe 4 | 503;Surheide - Hauptbahnhof - Elbinger Platz - Stadtverwaltung - Flötenkiel - Leherheide West 5 | 504;Hauptbahnhof - Konrad-Adenauer-Platz - Fischereihafen - Bohmsiel - IKEA 6 | 505;Gewerbegebiet Debstedt - Langen Mitte - Hinschweg - Flötenkiel - Lehe - Rotersand - Havenwelten - Hauptbahnhof - Wulsdorf - IKEA 7 | 506;SZ Langen - Langen Mitte - Flötenkiel - Lehe - Rotersand - Havenwelten - Wulsdorf - Wulsdorf Bahnhofstr. 8 | 507;Bramel - Schiffdorf - Hauptbahnhof - Elbinger Platz - Stadtverwaltung - Bahnhof Lehe - Spaden 9 | 508;Klinikum Bremerhaven - Hauptbahnhof - Havenwelten - Lehe - Bahnhof Lehe - Leherheide West 10 | 509;Langen -/Imsum Ort - Weddewarden - Flötenkiel - Stadtverwaltung - Havenwelten - Hauptbahnhof - Surheide 11 | 510;Am Seedeich - Fähranleger - Elbinger Platz - Hauptbahnhof 12 | 511;Bohmsiel - Wulsdorf - Weserfähre - Rotersand - Lehe - Gesundheitsamt - Parkstr. - Ahornweg - Leherheide West (Schnellbus) 13 | 512;(ALT) Weddewarden Überseering - Überseehafen - Rotersand 14 | 513;ALT Leherheide Louise-Schroeder-Str. - Plätternweg 15 | 515;ALT Stadtverwaltung - Buschkämpen 16 | 516;ALT Bohmsiel - Labradorhafen - Seewindstr. 17 | 517;ALT Surheide - Veerenholzstr. 18 | HL;Hafen-Liner: Thünen-Institut - Fischereihafen - Konrad-Adenauer-Platz - Hauptbahnhof - Elbinger Platz - Havenwelten - Neuer Hafen - Rotersand 19 | ML;Moon-Liner: Leherheide - Parkstr. - Lehe - Rotersand - Elbinger Platz - Hauptbahnhof - Humboldtschule - Wulsdorf - Surheide - Hauptbahnhof 20 | NL;Night-Liner: Debstedt - Langen Mitte - Parkstr. - Stadtverwaltung - Havenwelten - Hauptbahnhof - Konrad-Adenauer-Platz - Wulsdorf - Bohmsiel 21 | E11;Debstedt - Langen Mitte - Parkstr. - Flötenkiel - Stadtverwaltung - Hauptbahnhof - Gymnasium Wesermünde 22 | E13;Schiffdorf - Veerenholzstr. - Gymnasium Wesermünde 23 | E16;Leherheide West - Parkstr. - SZ Carl v. Ossietzky 24 | -------------------------------------------------------------------------------- /sources/linien_dd.csv: -------------------------------------------------------------------------------- 1 | Linie,Laufweg 2 | S1,Schöna – Bad Schandau – Pirna – Dresden – Coswig – Meißen Triebischtal 3 | S2,Pirna – Heidenau – Dresden Hbf – Dresden Flughafen 4 | S3,Dresden – Tharandt – Klingenberg-Colmnitz – Freiberg (Sachs) 5 | 1,Prohlis – Dobritz – Gruna – Stadtzentrum – Friedrichstadt – Leutewitz 6 | 2,Kleinzschachwitz – Leuben – Gruna – Stadtzentrum – Bahnhof Mitte – Cotta – Gorbitz 7 | 3,Coschütz – Plauen – Hauptbahnhof – Stadtzentrum – Bahnhof Neustadt – Wilder Mann 8 | 4,Laubegast – Tolkewitz – Striesen – Stadtzentrum – Mickten – Radebeul – Coswig – Weinböhla 9 | 6,Niedersedlitz – Leuben – Laubegast – Blasewitz – Johannstadt – Neustadt – Bahnhof Mitte – Löbtau – Wölfnitz 10 | 7,Weixdorf – Klotzsche – Neustadt – Stadtzentrum – Hauptbahnhof – Bahnhof Mitte – Cotta – Gorbitz – Pennrich 11 | 8,Hellerau – Neustadt – Stadtzentrum – Hauptbahnhof – Südvorstadt 12 | 9,Prohlis – Reick – Strehlen – Hauptbahnhof – Stadtzentrum – Mickten – Kaditz 13 | 10,Striesen – Pohlandplatz – Straßburger Platz – Hauptbahnhof – MESSE DRESDEN 14 | 11,Bühlau – Bahnhof Neustadt – Stadtzentrum – Hauptbahnhof – Zschernitz 15 | 12,Striesen – Blasewitz – Fetscherplatz – Stadtzentrum – Löbtau – Leutewitz 16 | 13,Prohlis – Reick – Strehlen – Straßburger Platz – Neustadt – Mickten (-Kaditz) 17 | 61,Weißig/Fernsehturm – Bühlau – Blasewitz – Gruna – Strehlen – Südvorstadt – Löbtau 18 | 62,Johannstadt – Stadtzentrum – Südvorstadt – Löbtau Süd – Naußlitz – Dölzschen 19 | 63,Pillnitz – Loschwitz – Blasewitz – Striesen – Strehlen – Mockritz – Zschernitz – Plauen – Löbtau 20 | 64,Reick – Gruna – Striesen – Universitätsklinikum – Waldschlößchen – Neustadt – Pieschen – Mickten – Kaditz 21 | 65,Heidenau/Luga – Leuben – Reick – Seidnitz – Blasewitz 22 | 66,Lockwitz/Nickern – Prohlis – Strehlen – Hauptbahnhof – Südhöhe – Mockritz/Coschütz (- Freital) 23 | 68,Goppeln – Leubnitz – Strehlen – Postplatz – Bahnhof Mitte – Friedrichstadt – Cotta – Cossebaude – Niederwartha 24 | 70,Bf. Klotzsche – Hellerau – Trachenberge – Trachau – Mickten - Übigau – Cotta – Gompitz 25 | 72,Klotzsche Infineon – Hellerau – Boxdorf – Radebeul – Altkaditz – ElbePark 26 | 73,Wilder Mann – Döbelner Straße – S-Bahnhof Pieschen – Wurzener Straße 27 | 74,Jägerpark – Waldschlößchen – Marienallee 28 | 76,Justizvollzugsanstalt – S-Bahnhof Pieschen 29 | 77,Klotzsche Infineon – Flughafen (– Industriegebiet Nord | Marsdorf) 30 | 78,(Ottendorf-Okrilla | Radeberg) – Langebrück – Klotzsche – Wilschdorf 31 | 79,Mickten – Übigau 32 | 80,Klotzsche – Wilschdorf – Boxdorf – Wilder Mann – Trachau – Übigau – Cotta – Omsewitz 33 | 81,Bahnhof Neustadt – Liststraße – Wilschdorf 34 | 83,Bonnewitz – Graupa – Pillnitz 35 | 84,Bühlau – Rochwitz – Loschwitz – Blasewitz 36 | 85,Striesen – Gruna – Strehlen – Zschernitz – Plauen – Löbtau Süd 37 | 86,Heidenau – Kleinzschachwitz – Laubegast – Dobritz – Prohlis – Lockwitz – Kreischa 38 | 87,Striesen – Tolkewitz – Seidnitz – Reick – Leubnitz – Mockritz 39 | 88,Kleinzschachwitz – Niedersedlitz – Prohlis – Kauscha 40 | 89,Röhrsdorf – Borthen – Lockwitz – Niedersedlitz 41 | 90,Löbtau – Naußlitz – Pesterwitz – Altfranken – Gompitz 42 | 91,Gompitz – Pennrich – Unkersdorf – Brabschütz – Merbitz – Briesnitz – Cotta 43 | 92,Cotta – Briesnitz – Ockerwitz 44 | 93,Oberwartha – Cossebaude – Mobschatz – Briesnitz – Cotta 45 | 95,Anruflinientaxi: Bahnhof Cossebaude – Gohlis – Bahnhof Cossebaude 46 | 97,Anruflinientaxi: Leutewitz – Zschonergrundmühle 47 | 98A,Weißig – Gönnsdorf – Pappritz – Niederpoyritz 48 | 98B,Weißig – Schönfeld – Rockau – Cunnersdorf – Niederpoyritz 49 | 98C,Schönfeld – Borsberg 50 | -------------------------------------------------------------------------------- /sources/linien_delbus.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 201;Bremen-Huchting - Heidkrug - Bahnhof/ZOB - Markt - Hasport - Annenheide 3 | 202;Deichhorst Hilversumer Str. - Markt - Bahnhof/ZOB - Lange Str. - Brendel - Annenheide - Hasport 4 | 203;Deichhorst Hanse-Wissenschaftskolleg - Neue Str. - Bahnhof/ZOB - Lange Str. - Berliner Str. - Hasport (- Annenheide) 5 | 204;Bremen-Huchting - Varrel - Stickgras - Bahnhof/ZOB - Markt - Düsternort - Brendel Jenaer Str./- Adelheide 6 | 205;Bungerhof - Nordstr. - Bahnhof/ZOB - Markt - Brendel Gothaer Str. 7 | 206;Bungerhof - Scheffelstr. - Bahnhof/ZOB - Markt - Berufsschule - Deichhorst Hanse-Wissenschaftskolleg 8 | 207;Hasbergen -/Alrusch - Dreilinien - Wendenstr. - Bahnhof/ZOB - Bergfeld - Elmeloh/- Wichernstift 9 | 212;Dreilinien - Wendenstr. - Museum - Bahnhof/ZOB - Markt - Brendel - Annenheide 10 | 213;Dwoberg - Bergfeld - Bahnhof/ZOB - Markt - Berliner Str. - Stickgras 11 | 214;Bremen-Huchting - Varrel - Heidkrug - Stickgras - Museum - Bahnhof/ZOB 12 | 215;Bungerhof - Scheffelstr. - Bahnhof/ZOB - Lange Str. - Brendel Gothaer Str. 13 | 216;Deichhorst Hilversumer Str. - Hanse-Wissenschaftskolleg - Markt - Bahnhof/ZOB - Lange Str. - Hasport 14 | 218;Wichernstift - Berufsschule - Bahnhof/ZOB - Lange Str. - Düsternort - Adelheide 15 | -------------------------------------------------------------------------------- /sources/linien_koeln.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 1;Weiden West – Junkersdorf – Müngersdorf – Aachener Str./Gürtel – Rudolfplatz – Neumarkt – Bf Deutz – Kalk – Höhenberg – Merheim – Brück – Refrath – Bensberg 3 | 3;Görlinger-Zentrum – Mengenich – Bocklemünd – Bickendorf – Ehrenfeld – Bf West – Friesenplatz – Neumarkt – Bf Deutz – Buchforst – Buchheim – Holweide – Dellbrück – Thielenbruch 4 | 4;Bocklemünd – Bickendorf – Ehrenfeld – Bf West – Friesenplatz – Neumarkt – Bf Deutz – Mülheim Wiener Platz – Höhenhaus – Dünnwald – Schlebusch 5 | 5;Am Butzweilerhof – Neuehrenfeld – Bf West – Friesenplatz – Dom/Hbf – Rathaus – Heumarkt (Teilstrecke: Nord-Süd-Stadtbahn) 6 | 7;Frechen – Marsdorf – Lindenthal – Aachener Str./Gürtel – Rudolfplatz – Neumarkt – Deutz – Poll – Westhoven – Ensen – Porz – Zündorf 7 | 9;Sülz – Universität – Bf Süd – Neumarkt – Bf Deutz – Kalk – Vingst – Ostheim – Königsforst 8 | 12;Merkenich – Niehl – Weidenpesch – Nippes – Ebertplatz – Friesenplatz – Rudolfplatz – Barbarossaplatz – Zollstock 9 | 13;Gürtelbahn: Sülzgürtel – Lindenthal – Aachener Str./Gürtel – Ehrenfeld – Neuehrenfeld – Bilderstöckchen – Nippes – Amsterdamer Str./Gürtel – Mülheim Wiener Platz – Bf Mülheim – Buchheim – Holweide Vischeringstr. 10 | 15;Chorweiler – Heimersdorf – Longerich – Weidenpesch – Nippes – Ebertplatz – Friesenplatz – Rudolfplatz – Barbarossaplatz – Chlodwigplatz – Ubierring 11 | 16;Rheinuferbahn: Niehl Sebastianstr. – Amsterdamer Str./Gürtel – Ebertplatz – Dom/Hbf – Neumarkt – Barbarossaplatz – Chlodwigplatz – Ubierring – Bayenthal – Rodenkirchen – Sürth – Godorf – Wesseling – Hersel – Bonn Hbf – Bonn-Bad Godesberg 12 | 17;Nord-Süd-Stadtbahn: Severinstr. – Chlodwigplatz – Rodenkirchen (– Sürth) 13 | 18;Vorgebirgsbahn: Thielenbruch – Dellbrück – Holweide – Buchheim – Bf Mülheim – Mülheim Wiener Platz – Zoo/Flora – Ebertplatz – Dom/Hbf – Neumarkt – Barbarossaplatz – Sülzgürtel – Klettenberg – Hürth – Brühl – Schwadorf – Bornheim – Alfter – Bonn Hbf 14 | E;Sonderzüge - meist zu Karneval oder zu Spielen des @fckoeln 15 | -------------------------------------------------------------------------------- /sources/linien_mv.csv: -------------------------------------------------------------------------------- 1 | Linie;Name/Laufweg/Betreiber 2 | RE1;Hanse-Express: Hamburg Hbf - Büchen - Hagenow Land - Schwerin Hbf - Bad Kleinen - Rostock Hbf (DB Regio Nordost) 3 | RE2;Wismar - Schwerin Hbf - Ludwigslust - Wittenberge - Berlin Hbf - Lübben (Speewald) - Cottbus Hbf (DB Regio Nordost) 4 | RE3;Stralsund Hbf - Greifswald - Züssow - Anklam - Pasewalk - Angermünde - Eberswalde - Berlin - Jüterbog - Falkenberg (Elster) (DB Regio Nordost) 5 | RE4;Stadttore-Linie: Lübeck Hbf - Bad Kleinen - Güstrow - Neubrandenburg - Pasewalk - Szczecin Główny / Ueckermünde Stadthafen (DB Regio Nordost) 6 | RE5;Rostock Hbf - Güstrow - / Stralsund Hbf - Neubrandenburg - Neustrelitz Hbf - Berlin Hbf - Wünsdorf-Waldstadt - Elsterwerda (/ Finsterwalde (Niederlausitz)) (DB Regio Nordost) 7 | RE7;Stralsund Hbf - Greifswald (DB Regio Nordost) 8 | RE9;Rostock Hbf - Ribnitz-Damgarten West - Velgast - Stralsund Hbf - Bergen auf Rügen - Lietzow - Sassnitz / Ostseebad Binz (ODEG) 9 | RE10;(Rostock Hbf - Ribnitz-Damgarten West - Velgast -) Stralsund Hbf - Greifswald - Züssow (ODEG) 10 | RB11;Wismar - Neubukow - Bad Doberan - Rostock Hbf - Sanitz - Tessin (DB Regio Nordost) 11 | RB12;(Bad Doberan -) Rostock Hbf - Rövershagen - Graal-Müritz / (Ribnitz-Damgarten West) (DB Regio Nordost) 12 | RB13;Rehna - Gadebusch - Schwerin Hbf- Crivitz - Parchim (ODEG) 13 | RB14;Hagenow Stadt - Hagenow Land - Ludwigslust - Neustadt-Glewe - Parchim (ODEG) 14 | RB15;Waren (Müritz) - Jabel (Meckl) - Inselstadt Malchow (- Karow (Meckl)) (HANS) 15 | RB16;Kleinseenbahn: Neustrelitz Hbf - Wesenberg - Mirow (HANS) 16 | RB17;(Rostock Hbf) / Wismar - Bad Kleinen - Schwerin Hbf - Ludwigslust (DB Regio Nordost) 17 | RB18;Bad Kleinen - Schwerin Hbf (DB Regio Nordost) 18 | RB19;Parchim - Lübz - Karow (Meckl) - Plau am See (ODEG) 19 | RB23;Züssow - Wolgast - Zinnowitz - Seebad Heringsdorf - Świnoujście Centrum (DB Regio Nordost) 20 | RB24;Zinnowitz - Karlshagen - Peenemünde (DB Regio Nordost) 21 | RB25;Velgast - Barth (DB Regio Nordost) 22 | RB26;Zu(g)bringer: Bergen auf Rügen - Putbus - Lauterbach Mole (PRESS) 23 | RB31;Molli: Bad Doberan - Heiligendamm - Ostseebad Kühlungsborn West (Mecklenburgische Bäderbahn Molli) 24 | RB32;Rasender Roland: Lauterbach Mole - Putbus - Binz LB - Sellin Ost - Baabe - Göhren (Rügen) (PRESS) 25 | RB74;(Pritzwalk West -) Pritzwalk - Brügge (Prignitz) - Meyenburg (- Plau am See) (HANS) 26 | S1;S-Bahn Rostock: Warnemünde - Rostock-Lütten Klein - Rostock-Marienehe - Rostock Hbf (DB Regio Nordost) 27 | S2;S-Bahn Rostock: Warnemünde - Rostock-Lütten Klein - Rostock-Marienehe - Rostock Hbf - Schwaan - Güstrow (DB Region Nordost) 28 | S3;S-Bahn Rostock: Warnemünde - Rostock-Lütten Klein - Rostock-Marienehe - Rostock Hbf - Laage (Meckl) - Priemerburg - Güstrow (DB Regio Nordost) 29 | -------------------------------------------------------------------------------- /sources/linien_ni.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | RE1;Hannover - Nienburg (Weser) - Verden (Aller) - Bremen - Delmenhorst - Oldenburg (Oldb) - Leer (Ostfriesl) - Emden - Norddeich 3 | RE2;Uelzen - Celle - Hannover - Sarstedt - Kreiensen - Northeim (Han) - Göttingen 4 | RE3;Hamburg - Lüneburg - Uelzen - Celle - Hannover 5 | RE4;Hamburg - Buchholz (Nordh) - Rotenburg (Wümme) - Bremen 6 | RE5;Hamburg - Buxtehude - Stade - Cuxhaven 7 | RE8;Hannover - Nienburg (Weser) - Verden (Aller) - Bremen - Osterholz-Scharmbek - Bremerhaven 8 | RE9;Bremerhaven - Osterholz-Scharmbek - Bremen - Diepholz - Osnabrück 9 | RE10;Bad Harzburg - Goslar - Salzgitter-Ringelheim - Hildesheim - Hannover 10 | RE15;Emden - Leer (Ostfriesl) - Meppen - Lingen (Ems) - Rheine - Münster (Westf) 11 | RE18;Wilhelmshaven - Oldenburg (Oldb) - Cloppenburg - Bramsche - Osnabrück 12 | RE19;Wilhelmshaven - Oldenburg (Oldb) - Delmenhorst - Bremen 13 | RE20;Uelzen - Salzwedel - Hohenwulsch - Stendal - Tangerhütte - Magdeburg 14 | RE21;Goslar - Vienenburg - Wernigerode - Halberstadt - Oschersleben (Bode) - Magdeburg 15 | RE30;Wolfsburg - Gifhorn - Lehrte - Hannover 16 | RE50;Wolfsburg - Braunschweig - Hildesheim 17 | RE60;Braunschweig - Peine - Hannover - Minden (Westf) - Melle - Osnabrück - Rheine 18 | RE70;Braunschweig - Peine - Hannover - Minden (Westf) - Herford - Bielefeld 19 | RE78;Nienburg (Weser) - Minden (Westf) - Herford - Bielefeld 20 | RE83;Lüneburg - Büchen - Lübeck - Kiel 21 | RB31;Hamburg - Maschen - Lüneburg 22 | RB32;Lüneburg - Dahlenburg - Dannenberg 23 | RB33;Buxtehude - Bremervörde - Bremerhaven - Dorum - Cuxhaven 24 | RB35;Wolfsburg - Oebisfelde - Gardelegen - Stendal 25 | RB36;Wolfsburg - Oebisfelde - Wegenstedt - Haldensleben - Barleben - Magdeburg 26 | RB37;Bremen - Langwedel - Soltau - Uelzen 27 | RB38;Bucholz (Nordh) - Soltau - Walsrode - Hannover 28 | RB40;Braunschweig - Helmstedt - Eilsleben - Magdeburg - Burg 29 | RB41;Hamburg - Buchholz (Nordh) - Tostedt - Scheeßel - Rotenburg (Wümme) - Bremen 30 | RB42;Braunschweig - Wolfenbüttel - Vienenburg - Bad Harzburg 31 | RB43;Braunschweig - Wolfenbüttel - Vienenburg - Goslar 32 | RB44;Braunschweig - Salzgitter-Immendorf - Salzgitter-Lebenstedt 33 | RB45;Braunschweig - Wolfenbüttel - Schöppenstedt 34 | RB46;Braunschweig - Seesen - Herzberg (Harz) 35 | RB47;Braunschweig - Gifhorn - Wittingen - Uelzen 36 | RB48;Braunschweig - Salzgitter-Lebenstedt 37 | RB56;Bad Bentheim - Nordhorn - Neuenhaus 38 | RB57;Leer - Weener - Nieuweschanz - Groningen 39 | RB58;Osnabrück - Bramsche - Vechta - Delmenhorst - Bremen 40 | RB59;Wilhelmshaven - Sande - Jever - Wittmund - Esens 41 | RB61;Bielefeld - Herford - Bünde - Osnabrück - Rheine - Bad Bentheim - Hengelo 42 | RB65;Bad Bentheim - Nordhorn - Neuenhaus 43 | RB66;Osnabrück - Hasbergen - Lengerich - Münster (Westf) 44 | RB75;Osnabrück - Dissen-Bad Rothenfelde - Halle (Westf) - Bielefeld 45 | RB77;Hildesheim - Hameln - Löhne - Bünde 46 | RB79;Hildesheim - Bodenburg 47 | RB80;Göttingen - Nordheim - Herzberg (Harz) - Nordhausen 48 | RB81;Bodenfelde - Northeim - Herzberg (Harz) - Nordhausen 49 | RB82;Göttingen - Northeim - Kreiensen - Seesen - Goslar - Bad Harzburg 50 | RB84;Kreiensen - Holzminden - Höxter - Ottbergen - Paderborn 51 | RB85;Göttingen - Bodenfelde - Ottbergen - Paderborn 52 | RB86;Göttingen - Northeim - Einbeck-Salzderhelden - Einbeck 53 | RB83;Göttingen - Eichenberg - Hann Münden - Kassel 54 | RB87;Göttingen - Eichenberg - Bebra - Bad Hersfeld - Fulda 55 | S1;Minden (Westf) - Stadthagen - Wunstorf - Hannover - Weetzen - Haste 56 | S2;Nienburg (Weser) - Wunstorf - Hannover - Weetzen - Haste 57 | S3;Hildesheim - Lehrte - Hannover 58 | S4;Hildesheim - Sarstedt - Hannover Messe/Laatzen - Hannover - Langenhagen - Bennemühlen 59 | S5;Hannover Flughafen - Langenhagen - Hannover - Bad Pyrmont - Altenbeken - Paderborn 60 | S6;Celle - Burgdorf - Hannover 61 | S7;Celle - Burgdorf - Lehrte - Hannover 62 | RS1;Verden (Aller) - Achim - Bremen - Bremen-Vegesack - Bremen-Farge 63 | RS2;Bremerhaven-Lehe - Osterholz-Scharmbeck - Ritterhude - Bremen - Twistringen 64 | RS3;Bremen - Delmenhorst - Hude - Oldenburg (Oldb) - Bad Zwischenahn 65 | RS4;Bremen - Delmenhorst - Hude - Nordenham 66 | RS6;Rotenburg (Wümme) - Verden (Aller) 67 | -------------------------------------------------------------------------------- /sources/linien_no.csv: -------------------------------------------------------------------------------- 1 | Linje;Vei 2 | F1;Oslo - Karlstad - Stockholm 3 | F4;Bergensbane: Oslo S - Drammen - Finse - Myrdal - Voss - Arna - Bergen 4 | F5;Sørlandsbana: Oslo - Drammen - Kongsberg - Nelaug - Kristiansand - Egersund - Stavanger 5 | F6;Dovrebane: Oslo - Hamar - Lillehammer - Dombås- Støren - Trondheim S 6 | F7;Nordlandsbane: Trondheim - Steinkjer - Mosjøen - Rognan - Bodø 7 | F8;Ofotbane: Narvik - Bjørnfjell (- Kiruna) 8 | FLY1;Flytoget: Drammen - Oslo S - Lillestrøm - OSL (stopper ikke på Stabekk) 9 | FLY2;Flytoget: Drammen - Stabekk - Oslo S - OSL (stopper ikke på Lillestrøm) 10 | L1;Spikkestad - Asker - Oslo S - Lillestrøm 11 | L2;Stabekk - Oslo S - Ski 12 | L4;Arnabanen: Arna - Bergen 13 | L5;Egersund - Stavanger 14 | RE10;Drammen - Asker - Oslo S - OSL - Eidsvoll - Hamar - Lillehammer (- Dombås) 15 | RE11;Vestfoldbanen: Skien - Larvik - Drammen - Asker - Oslo S - OSL - Eidsvoll 16 | R12;Kongsberg - Frammen - Asker - Oslo S - OSL - Eidsvoll 17 | R13;Drammen - Asker - Oslo S - Lillestrøm - Dal 18 | R14;Asker - Oslo S - Lillestrøm - Kongsvinger 19 | RE20;Østfoldbanen: Göteborg - Halden - Moss - Ski - Oslo S 20 | R21;Moss - Ski - Oslo S 21 | R22;Rakkestad - Mysen - Ski - Oslo S 22 | R23;Follobanen: Ski - Oslo S 23 | RE30;Gjøvikbanen: Oslo S - Nittedal - Jaren - Gjøvik 24 | RE31;Gjøvikbanen: Oslo S - Nittedal - Jaren 25 | R40;Vossebana: Myrdal - Voss - Arna - Bergen 26 | R45;Flåmsbana: Myrdal - Flåm 27 | R50;Arendalsbanen: Nelaug - Arendal 28 | R55;Bratsbergbanen: Notodden - Nordagutu - Skien - Porsgrunn 29 | R60;Rørosbanen: Hamar - Røros - Støren - Trondheim S 30 | R65;Raumabana: Dombås - Åndalsnes 31 | R70;(Støren / Lerkendal) - Trondheim S - Steinkjer) 32 | R71;Meråkerbane: Trondheim - Storlien 33 | R75;Rognan - Fauske - Bodø 34 | -------------------------------------------------------------------------------- /sources/linien_nvs.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 1;Kliniken - Hauptbahnhof - Marienplatz - Stauffenbergstraße - Berliner Platz - Hegelstraße 3 | 2;Lankow-Siedlung - Platz der Freiheit - Marienplatz - Stauffenbergstraße - Berliner Platz - Hegelstraße 4 | 3;Hegelstraße - Berliner Platz - Stauffenbergstraße - Waldfriedhof - Krebsförden - Neu Pampow 5 | 4;Kliniken - Hauptbahnhof - Marienplatz - Waldfriedhof - Krebsförden - Neu Pampow 6 | 5;Hauptbahnhof P+R - Marienplatz - Görries 7 | 6;Stauffenbergstraße - Freilichtmuseum - Raben Steinfeld, Oberdorf 8 | 7;Hauptbahnhof P+R - Marienplatz - Krebsförden Dorf - Grabenstraße - Krebsförden 9 | 8;Lübstorf - Wickendorf - Kliniken - Hauptbahnhof - Marienplatz - Jugendherberge 10 | 9;Stauffenbergstraße - Am Grünen Tal - Stern Buchholz 11 | 10;Buchenweg - Knaudtstraße - Schloss/Theater - Marienplatz - Hauptbahnhof - Platz der Freiheit - Alter Friedhof - Bleicherufer 12 | 11;Buchenweg - / Kliniken - Walther-Rathenau-Straße - Bergstraße - Schelfmarkt - Hauptbahnhof - Platz der Freiheit - Alter Friedhof - Bleicherufer 13 | 12;Marienplatz - Neumühle - Sacktannen - Wittenförden 14 | 13;Lankow-Siedlung - Sacktannen - Grabenstraße - Krebsförden - Waldfriedhof - Am Grünen Tal - Otto von-Guericke-Straße 15 | 14;Lankow-Siedlung - Neumühle - Marienplatz - Jugendherberge 16 | 16;Kantstraße - Am Grünen Tal - Waldfriedhof - Krebsförden - Grabenstraße - Görries 17 | 17;Friedrichsthal - Lankow-Siedlung - Kieler Straße 18 | 18;Friedrichsthal - Lankow-Siedlung - Kieler Straße - Margaretenhof - Alte Gärtnerei - Pingelshagen 19 | 19;Hauptbahnhof P+R - Marienplatz - Betriebshof NVS 20 | 20;Stauffenbergstraße - Göhrener Tannen 21 | -------------------------------------------------------------------------------- /sources/linien_nwm.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 1;Stadtverkehr Wismar: Gägelow - Ostseeblick - Wendorf - Wismar ZOB - Krankenhaus - Friedenshof 3 | 2;Stadtverkehr Wismar: Gägelow - Proseken - Ostseeblick - Wendorf - Krankenhaus - Friedhof - ZOB 4 | 3;Stadtverkehr Wismar: Fischkaten - Philosophenweg - ZOB - Burgwall - Markt - Fischkaten 5 | 4;Stadtverkehr Wismar: Seebad Wendorf - Markt - ZOB - Sporthalle - Krankenhaus - Gartenstadt 6 | 5;Stadtverkehr Wismar: Seebad Wendorf - Burgwall - Markt - Bahnhof 7 | 6;Stadtverkehr Wismar: Kritzow - Dargetzow - ZOB - Westhafen - Burgwall - Kritzow 8 | 30;Stadtverkehr Grevesmühlen 9 | 106;Schwerin - Kl. Trebbow - Drispeth 10 | 107;Schwerin - Alt Meteln - Böken 11 | 110;Schwerin - Brüsewitz - Cramon 12 | 130;Gadebusch - Bobitz - Wismar 13 | 131;Gadebusch - Ratzeburg 14 | 140;Gadebusch - Schwerin 15 | 142;Gadebusch - Mühlen Eichsen - Grevesmühlen 16 | 143;Gadebusch - Mühlen Eichsen - Schwerin 17 | 144;Schwerin - Drieberg - Gadebusch 18 | 145;Gadebusch - Schönberg - Selmsdorf 19 | 146;Gadebusch - Rehna - Carlow - Schlagsdorf 20 | 147;Bentin - Krembz - Gadebusch 21 | 148;Krembz - Lützow - Schwerin 22 | 151;Grambow - Lützow - Gr. Welzin - Gadebusch 23 | 152;Gadebusch - Rosenow 24 | 153;Gadebusch - Lützow - Krembz 25 | 155;Gadebusch - Wakenstädt 26 | 156;Gadebusch - Kneese - Roggendorf - Gadebusch 27 | 158;Schwerin - Gadebusch - Rehna - Schönberg 28 | 160;Rehna - Parber - Törber - Löwitz 29 | 161;Gadebusch - Rehna - Benzin 30 | 162;Gadebusch - Meetzen - Kl. Hundorf - Gadebusch 31 | 163;Gadebusch - Webelsfelde/Frauenmark - Mühlen Eichsen 32 | 200;Wismar - Neukloster 33 | 202;Wismar - Neukloster - Warin - Groß Labenz 34 | 203;Kritzow - Madson Hof 35 | 230;Wismar - Kirchdorf - Timmendorf Strand 36 | 235;Schwerin Hbf - Lübstorf - Wismar 37 | 240;Wismar - Klütz - Boltenhagen - Weiße Wiek 38 | 241;Wismar - Hohen Wieschendorf 39 | 245;Wismar - Neukloster - Warin - Blankenberg 40 | 250;Wismar - Bobitz 41 | 251;Wismar - Beidendorf - Bobitz - Dorf Mecklenburg - Lübow 42 | 254;Rambow - Bobitz 43 | 255;Bobitz - Bad Kleinen 44 | 280;Dorf Mecklenburg - Gallentin 45 | 300;Grevesmühlen - Dassow - Schlutup 46 | 301;Dassow - Schönberg - Selmsdorf 47 | 310;Grevesmühlen - Mühlen Eichsen/Schönhof 48 | 320;Grevesmühlen - Tarnewitz/Redewisch 49 | 321;Klütz - Hohen Schönberg - (Schwansee) - Kalkhorst - Dassow 50 | 322;Klütz - Elmenhorst - Brook 51 | 323;Damshagen - Gutow - Moor/Parin 52 | 325;Weiße Wiek - Boltenhagen - Redewisch 53 | 330;Grevesmühlen - Wismar 54 | 331;Grevesmühlen - Friedrichshagen/Barendorf 55 | 332;Grevesmühlen - Niendorf/Ostsee 56 | 333;Grevesmühlen - Warnow - Damshagen 57 | 335;Grevesmühlen - Dassow - Selmsdorf - Lübeck 58 | 340;Grevesmühlen - Mühlen Eichsen - Schwerin Hbf 59 | 341;Grevesmühlen - Mallentin - Damshagen/Dassow 60 | 342;Grevesmühlen - Menzendorf - Schönberg 61 | 343;Grevesmühlen - Bernstorf - Börzow/Gostorf 62 | 344;Grevesmühlen - Rehna - Schönberg 63 | 345;Grevesmühlen - Klütz - Boltenhagen - Weiße Wiek 64 | 351;Schönberg - Carlow/Cordshagen 65 | 353;Schönberg - Lockwisch 66 | 372;Dassow - Harkensee - Pötenitz 67 | 390;Boltenhagen - Dassow - Schönberg - Herrnburg - Lübeck 68 | 390;Schönberg - Lüdersdorf - Herrnburg 69 | 391;Schattin - Herrnburg 70 | 400;Wismar - Proseken - (Barnekow) - Grevesmühlen 71 | 401;Wismar - Zierow - Proseken 72 | 402;Wismar - Barnekow - Gägelow - Proseken 73 | 410;Neukloster - Lüdersdorf b Neukloster 74 | 411;Kritzow - Neukloster Am Sonnenberg 75 | 412;Wismar - Hornstorf - Krusenhagen 76 | 413;Wismar - Neuburg - Boiensdorf 77 | 420;Wismar - Ventschow 78 | 421;Karow Gewerbepark - Kritzow Gewerbegebiet 79 | 431;Wismar - Boiensdorf 80 | 440;Neukloster - Groß Tessin - Babst - Glasin - Passee 81 | 441;Neukloster - Wakendorf 82 | -------------------------------------------------------------------------------- /sources/linien_opr.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 701;Stadtlinie Kyritz: Lindenschule - Untersee 3 | 702;Kyritz - Teetz 4 | 703;Kyritz - Berlitt - Breddin 5 | 704;Kyritz - Blankenberg - Gottberg - Neuruppin 6 | 705;Kyritz - Babe 7 | 706;Kyritz - Joachimshof - Babe 8 | 707;Kyritz - Schönberg (Ky.) - Wulkow (Ky.) 9 | 711;PlusBus Ruppiner Seenland: Kyritz - Wusterhausen - Neuruppin 10 | 712;Kyritz - Zernitz - Neustadt (Dosse) 11 | 713;Neustadt (Dosse) - Wusterhausen - Nackel 12 | 714;Kyritz - Wusterhausen (Dosse) - Neustadt (Dosse) 13 | 715;Neustadt (Dosse) - Sieversdorf - Dreetz 14 | 717;Kyritz - Drewen 15 | 719;Breddin - Zernitz - Neustadt (Dosse) 16 | 740;Stadtlinie Wittstock (Dosse): Alt Daber - Polthierstr. 17 | 741;Wittstock - Gadow - Dossow - Wittstock 18 | 742;Wittstock - Blumenthal - Grabow - Herzsprung 19 | 743;Wittstock - Blesendorf - Heiligengrabe - Wittstock 20 | 744;Wittstock - Königsberg - Herzsprung - Rossow - Kyritz 21 | 745;Wittstock - Freyenstein - Ackerfelde - Meyenburg 22 | 746;Wittstock - Sewekow - Zempow - Flecken Zechlin 23 | 748;Rossow - Neuruppin 24 | 752;Neuruppin - Garz - Wustrau 25 | 754;Neuruppin - Kränzlin - Walsleben - Dannenfeld 26 | 756;PlusBus Ruppiner Seenland: Neuruppin - Fehrbellin 27 | 757;Fehrbellin - Königshorst 28 | 758;Fehrbellin - Linum - Kremmen 29 | 759;Neuruppin - Wildberg 30 | 762;Neuruppin - Gühlen Glienicke - Rägelin - Walsleben 31 | 764;PlusBus Ruppiner Seenland: Neuruppin - Lindow (Mark) - Rheinsberg 32 | 766;Wustrau - Fehrbellin 33 | 770;Stadtlinie Neuruppin: Treskow - Alt Ruppin 34 | 771;Stadtlinie Neuruppin: Alt Ruppin - W.-Rathenau-Str. 35 | 772;Stadtlinie Neuruppin: Rheinsberger Tor Bhf. - Alt Ruppin, Schule 36 | 777;Neuruppin - Rüthnick - Wustrau 37 | 779;Neuruppin - Molchow - Zermützel/Zippelsförde 38 | 782;Wall - Beetz-Sommerfeld 39 | 783;Herzberg (Mark) - Löwenberg 40 | 784;Rheinsberg - Lindow (Mark) - Gransee 41 | 785;Schlösser-Linie: Rheinsberg - Flecken Zechlin - Mirow 42 | 787;Neuruppin - Flecken Zechlin 43 | 788;Rheinsberger Seenbus: Rheinsberg - Großzerlang 44 | 791;Neuruppin - Rüthnick - Herzberg (Mark) - Lindow (Mark) 45 | 792;Lindow (Mark) - Gühlen - Keller - Banzendorf - Hindenberg (OPR) 46 | 794;Tierpark-Heide-Linie: Neuruppin - Gühlen Glienicke - Rheinsberg 47 | -------------------------------------------------------------------------------- /sources/linien_sh.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | RE1;Hanseexpess: Hamburg Hbf - Schwarzenbek - Müssen - Büchen - Schwerin Hbf - Bad Kleinen - Rostock Hbf 3 | RE4;Lübeck Hbf - Bad Kleinen - Güstrow - Neubrandenburg - Pasewalk - Szczecin Glowny 4 | RE6;Hamburg-Altona - Elmshorn -Glückstadt - Itzehoe - Husum - Niebüll - Westerland (Sylt) 5 | RE7;Hamburg Hbf - Elmshorn - Neumünster < Bordesholm - Kiel | Rendsburg - Schleswig - Flensburg 6 | RE8;Hamburg Hbf - Bad Oldesloe - Reinfeld - Lübeck Hbf - Lübeck-Travemünde Strand 7 | RE60;Sprinter: Hamburg Hbf - Husum - Niebüll - Westerland (Sylt) 8 | RE70;Hamburg Hbf - Elmshorn - Neumünster - Bordesholm - Kiel Hbf 9 | RE72;Kiel Hbf- Eckernförde - Süderbrarup - Flensburg 10 | RE74;Kiel Hbf - Rendsburg - Schleswig - Husum 11 | RE80;Hamburg Hbf - Ahrensburg - Bad Oldesloe - Reinfeld - Lübeck Hbf 12 | RE83;Kiel Hbf - Plön - Eutin - Lübeck Hbf - Ratzeburg - Büchen - Lüneburg 13 | RE84;Sprinter: Hamburg Hbf - Lübeck Hbf - Bad Schwartau - Eutin - Plön - Preetz - Kiel Hbf 14 | RE85;Hamburger Strand-Express: Hamburg Hbf - Ahrensburg - Bad Oldesloe - Lübeck Hbf - Oldenburg (Holst) - Fehmarn-Burg - Puttgarden 15 | RB61;Hamburg Hbf - Pinneberg - Elmshorn - Glückstadt - Itzehoe 16 | RB62;Heide (Holst) - Meldorf - St. Michaelisdonn - Wilster - Itzehoe 17 | RB63;Neumünster - Hohenwestedt - Nordhastedt - Heide (Holst) - Büsum 18 | RB64;Husum - Tönning - Katharinenheerd - Tating - Bad St. Peter-Ording 19 | RB65;Niebüll - Deezbüll - Maasbüll - Dagebüll 20 | RB66;Niebüll - Uphusum - Süderlügum - Tonder - Visby - Rejsby - Esbjerg 21 | RB71;Hamburg-Altona - Pinneberg - Elmshorn - Wrist 22 | RB73;Kiel Hbf - Eckernförde 23 | RB75;Kiel Hbf - Rendsburg 24 | RB76;Kiel Hbf - Kiel-Oppendorf 25 | RB81;Hamburg Hbf - Hamburg-Wandsbek - Ahrensburg - Bargteheide - Bad Oldesloe 26 | RB82;Bad Oldesloe - Bad Segeberg - Neumünster 27 | RB84;Kiel Hbf - Preetz - Plön - Eutin - Bad Schwartau - Lübeck Hbf 28 | RB85;Lübeck Hbf - Bad Schwartau - Scharbeutz - Sierksdorf - Neustadt (Holst) 29 | X85;Lübeck ZOB - Haffkrug - Oldenburg (Holst) - Fehmarn-Burg - Puttgarden 30 | RB86;Lübeck Hbf - Lübeck-Travemünde Strand 31 | A1;Hamburg-Eidelstedt - Ulzburg Süd 32 | A2;Norderstedt Mitte - Kaltenkirchen (Holst) - Neumünster 33 | A3;Elmshorn - Barmstedt - Ulzburg Süd 34 | -------------------------------------------------------------------------------- /sources/linien_sn.csv: -------------------------------------------------------------------------------- 1 | Linie,Laufweg 2 | RE1,Dresden – Arnsdorf – Bischofswerda – Bautzen – Löbau – Görlitz 3 | RB1,Zwickau Zentrum – Lengenfeld – Falkenstein – Klingenthal – Kraslice 4 | RE2,Dresden – Arnsdorf – Bischofswerda – Ebersbach – Zittau – Liberec 5 | RB2,Zwickau – Plauen – Mehltheuer – Hof / Cheb 6 | RE3,Dresden – Tharandt – Chemnitz – Zwickau – Plauen (Vogtl) – Hof 7 | RE4,Halle (Saale) Hbf – Domnitz (Saalkr) – Könnern – Halberstadt – Goslar 8 | RB4,Gera – Elsterberg – Weischlitz 9 | RB5,Mehltheuer – Plauen – Falkenstein – Kraslice – Karlovy Vary 10 | RE6,Leipzig – Bad Lausick – Geithain – Burgstädt – Chemnitz 11 | RE10,Leipzig Hbf – Eilenburg – Beilrode – Cottbus 12 | RE13,Leipzig Hbf – Delitzsch – Bitterfeld – Dessau Hbf – Zerbst (Anh) – Magdeburg Hbf 13 | RE15,Dresden – Großenhain – Ruhland – Hoyerswerda 14 | RE18,Dresden – Großenhain – Ortrand – Ruhland – Senftenberg – Cottbus 15 | RE19,Dresden – Heidenau – Altenberg (Ski- und Wanderexpress) 16 | RE20,Dresden – Pirna – Bad Schandau – Schöna – Dolní Zleb – Děčín – Ústí n.L. – Litoměřice 17 | RB20,Leipzig Hbf – Weißenfels – Naumburg (Saale) Hbf – Bad Kösen – Großheringen – Erfurt Hbf – Eisenach 18 | RB30,Dresden – Klingenberg-Colmnitz – Freiberg – Chemnitz – Glauchau – Zwickau 19 | RB31,Dresden – Cossebaude – Coswig – Großenhain – Elsterwerda-Biehla 20 | RB33,Dresden – Ottendorf-Okrilla – Königsbrück 21 | RB34,Dresden – Radeberg – Kamenz 22 | RB37,Gößnitz – Glauchau 23 | RE42,Leipzig Hbf – Weißenfels – Naumburg (Saale) Hbf – Jena – Saalfeld – Nürnberg 24 | RB45,Chemnitz – Stauchitz – Riesa – Elsterwerda 25 | RE50,Saxonia: Dresden – Riesa – Leipzig 26 | RB60,Dresden – Arnsdorf – Bischofswerda – Bautzen – Löbau – Görlitz 27 | RB61,Dresden – Arnsdorf – Bischofswerda – Ebersbach – Zittau 28 | RB64,Hoyerswerda – Uhyst – Niesky – Görlitz 29 | RB65,Zittau – Görlitz – Weißwasser – Cottbus 30 | RB71,Pirna – Dürrröhrsdorf – Neustadt – Sebnitz 31 | RB72,Heidenau – Glashütte – Altenberg 32 | RB80,Chemnitz – Flöha – Annaberg-Buchholz – Cranzahl 33 | RB81,Chemnitz – Flöha – Pockau-Lengefeld – Olbernhau-Grünthal 34 | RB89,Chemnitz – Thalheim – Aue (Schienenersatzverkehr mit Bus 361 und 524) 35 | RB95,Zwickau – Aue – Johanngeorgenstadt 36 | RB110,Leipzig – Grimma – Döbeln 37 | RB113,Leipzig – Bad Lausick – Geithain 38 | EBx12,Leipzig Hbf – Zeitz – Wetterzeube – Gera – Saalfeld 39 | EBx13,Gera – Weida – Hof 40 | EB22,Leipzig Hbf – Zeitz – Wetterzeube – Gera – Saalfeld 41 | U28,Rumburk – Šluknov – Sebnitz – Bad Schandau – Děčín 42 | T7,Cranzahl – Vejprty – Chomutov 43 | L7,Rybniště/Seifhennersdorf – Varnsdorf – Zittau – Liberec 44 | CD142|ČD142,Johanngeorgenstadt – Karlovy Vary 45 | CB53,Glauchau – Stollberg (Sachs) 46 | Kirnitzschtalbahn,Bad Schandau Stadtpark – Lichtenhainer Wasserfall 47 | LGB,Radebeul Ost – Radeburg 48 | Lößnitzgrundbahn,Radebeul Ost – Radeburg 49 | WTB,Freital-Hainsberg – Dippoldiswalde – Kurort Kipsdorf 50 | Weißeritztalbahn,Freital-Hainsberg – Dippoldiswalde – Kurort Kipsdorf 51 | Fichtelbergbahn,Cranzahl – Kurort Oberwiesenthal 52 | DSB,Augustusburg – Erdmannsdorf 53 | Drahtseilbahn,Augustusburg – Erdmannsdorf 54 | FEG,Freiberg – Holzhau 55 | Freiberger Eisenbahn,Freiberg – Holzhau 56 | DBG|DBG502|Döllnitzbahn,Oschatz – Mügeln – Altmügeln – Kemmlitz/Glossen 57 | SOEG|Zittauer Schmalspurbahn,Zittau – Bertsdorf – Kurort Oybin/Kurort Jonsdorf 58 | WEM|Waldeisenbahn|Waldeisenbahn Muskau,Weißwasser – Kromlau/Bad Muskau 59 | -------------------------------------------------------------------------------- /sources/linien_th.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg und Betreiber 2 | RE1;Göttingen – Erfurt – Gera – Glauchau (DB) 3 | RE2;Erfurt – Bad Langensalza – Leinefelde – Kassel-Wilhelmshöhe (DB) 4 | RE3;Erfurt – Jena – Gera – Altenburg/Greiz (DB) 5 | RE7;Erfurt – Grimmenthal – Würzburg (DB) 6 | RE14;Saalfeld (Saale) – Bamberg – Nürnberg (DB) 7 | RE18;Jena Göschwitz – Naumburg – Halle (DB) 8 | RE19;Sonneberg – Coburg – Bamberg – Nürnberg (DB) 9 | RE42;Leipzig – Jena – Saalfeld (Saale) – Nürnberg (DB) 10 | RE49;Sonneberg – Coburg – Lichtenfels – Nürnberg (DB) 11 | RE55;Nordhausen – Erfurt (DB) 12 | RE56;Nordhausen – Erfurt (DB) 13 | RB37;Gößnitz – Glauchau (DB) 14 | RB52;Erfurt – Leinefelde (DB) 15 | RB53;Gotha – Bad Langensalza (DB) 16 | RB80;Nordhausen – Göttingen (DB) 17 | S5X;Halle – Leipzig/Halle Flughafen – Leipzig – Altenburg – Zwickau (DB) 18 | S5;Halle – Leipzig – Altenburg – Zwickau (DB) 19 | RE8;Leinefelde – Sangerhausen – Halle (ABRM) 20 | RE9;Kassel – Nordhausen – Sangerhausen – Halle (ABRM) 21 | RE10;Erfurt – Sangerhausen – Magdeburg (ABRM) 22 | RE15;Saalfeld – Jena Saalbf (ABRM) 23 | RE16;Erfurt – Naumburg – Halle (ABRM) 24 | RE17;Erfurt – Naumburg (ABRM) 25 | RB20;Eisenach – Erfurt – Naumburg – Leipzig (ABRM) 26 | RB25;Saalfeld – Jena – Naumburg – Halle (ABRM) 27 | RB51;Heilbad Heiligenstadt – Leinefelde – Nordhausen (ABRM) 28 | RB59;Erfurt – Sangerhausen (ABRM) 29 | RB75;Nordhausen – Sangerhausen – Halle (ABRM) 30 | RE12;Leipzig – Gera – Saalfeld (Saale) (EIB) 31 | RE13;Gera – Zeulenroda unt Bf – Hof (EIB) 32 | RB21;Erfurt – Weimar – Jena – Gera (EIB) 33 | RB22;Leipzig – Gera – Saalfeld (Saale) (EIB) 34 | RB23;Erfurt – Arnstadt – Saalfeld (Saale) (EIB) 35 | RB26;Weimar – Kranichfeld (EIB) 36 | RB27;Sömmerda – Buttstädt (EIB) 37 | RB28;Jena Saalbf – Pößneck unt Bf (EIB) 38 | RB32;Saalfeld (Saale) – Blankenstein (Saale) (EIB) 39 | RB40;Meiningen – Schweinfurt (EIB) 40 | RE45;Erfurt – Ilmenau (STB) 41 | RE50;Erfurt – Zella-Mehlis – Meiningen (STB) 42 | RB41;Eisenach – Meiningen – Eisfeld – Sonneberg – Neuhaus a Rennweg (STB) 43 | RB43;Wernshausen – Schmalkalden – Zella-Mehlis – Suhl (STB) 44 | RB44;Erfurt – Meiningen (STB) 45 | RB46;Erfurt – Ilmenau– Rennsteig (STB) 46 | RB48;Fröttstädt – Friedrichroda (STB) 47 | OBS;Rottenbach – Katzhütte, Obstfelderschmiede – Lichtenhain (Bergbahn), Lichtenhain (Bergbahn) – Cursdorf (OBS) 48 | HSB;Nordhausen – Ilfeld – Drei Annen Hohne (HSB) 49 | RB4;Gera – Greiz – Weischlitz (VBG) 50 | RB6;Eisenach – Bebra (CAN) 51 | -------------------------------------------------------------------------------- /sources/linien_vab_kreis_ab.csv: -------------------------------------------------------------------------------- 1 | Linie;Start;über 1;über 2;über 3;über 4;über 5;über 6;;Ziel;Anmerkung;Laufweg 2 | 20;Aschaffenburg ROB;Goldbach;Hösbach;Feldkahl;;;;;Schöllkrippen;;Aschaffenburg ROB - Goldbach - Hösbach - Feldkahl - Schöllkrippen 3 | 21;Aschaffenburg ROB;Goldbach;Hösbach-Sand;Wenighösbach;;;;;Unterafferbach;;Aschaffenburg ROB - Goldbach - Hösbach-Sand - Wenighösbach - Unterafferbach 4 | 23;Aschaffenburg ROB;Goldbach;Feldkahl;;;;;;Mömbris;;Aschaffenburg ROB - Goldbach - Feldkahl - Mömbris 5 | 24;Aschaffenburg ROB;Steinbach;Oberafferbach;Rückersbach;Hohl;;;;Mömbris;;Aschaffenburg ROB - Steinbach - Oberafferbach - Rückersbach - Hohl - Mömbris 6 | 25;Aschaffenburg ROB;Johannesberg;Breunsberg;Daxberg;Mömbris;Geiselbach;;;Schöllkrippen;;Aschaffenburg ROB - Johannesberg - Breunsberg - Daxberg - Mömbris - Geiselbach - Schöllkrippen 7 | 26;Mömbris;Rappach;Molkenberg;;;;;;Hemsbach;;Mömbris - Rappach - Molkenberg - Hemsbach 8 | 27;Wiesen;Edelbach;Schöllkrippen;;;;;;Hösbach;;Wiesen - Edelbach - Schöllkrippen - Hösbach 9 | 28;Heigenbrücken;Jakobsthal;Heinrichsthal;Wiesen;Schöllkrippen;;;;Hösbach;;Heigenbrücken - Jakobsthal - Heinrichsthal - Wiesen - Schöllkrippen - Hösbach 10 | 29;Blankenbach;Krombach;Schöllkrippen;;;;;;Vormwald;;Blankenbach - Krombach - Schöllkrippen - Vormwald 11 | 29S;Vormwald;Schöllkrippen;Geiselbach;Krombach;;;;;Blankenbach;;Vormwald - Schöllkrippen - Geiselbach - Krombach - Blankenbach 12 | 30;Schöllkrippen;Westerngrund;Somborn;Bernbach;Horbach;Eidengesäß;;;Gelnhausen;;Schöllkrippen - Westerngrund - Somborn - Bernbach - Horbach - Eidengesäß - Gelnhausen 13 | 31;Aschaffenburg ROB;Kleinostheim Waldstadt;Dettingen;Alzenau;Albstadt;;;;Somborn;;Aschaffenburg ROB - Kleinostheim Waldstadt - Dettingen - Alzenau - Albstadt - Somborn 14 | 32;Kahl;Karlstein;Alzenau;;;;;;Kahl;;Kahl - Karlstein - Alzenau - Kahl 15 | 33;Schöllkrippen;;;;;;;;Aschaffenburg;Kahlgrundexpress;(Kahlgrundexpress) Schöllkrippen - Aschaffenburg 16 | 40;Aschaffenburg ROB;Haibach;Mespelbrunn;;;;;;Dammbach;;Aschaffenburg ROB - Haibach - Mespelbrunn - Dammbach 17 | 41;Aschaffenburg ROB;Bessenbach;;;;;;;Hösbach-Bahnhof;;Aschaffenburg ROB - Bessenbach - Hösbach-Bahnhof 18 | 42;Aschaffenburg ROB;Goldbach;Hösbach;Hösbach Bf.;Waldaschaff;Rothenbuch;;;Weibersbrunn;;Aschaffenburg ROB - Goldbach - Hösbach - Hösbach Bf. - Waldaschaff - Rothenbuch - Weibersbrunn 19 | 43;Aschaffenburg ROB;Haibach;Schmerlenbach;Hösbach Bf.;;;;;Waldaschaff;;Aschaffenburg ROB - Haibach - Schmerlenbach - Hösbach Bf. - Waldaschaff 20 | 44;Aschaffenburg ROB;Goldbach;Hösbach;Laufach;;;;;Hain;;Aschaffenburg ROB - Goldbach - Hösbach - Laufach - Hain 21 | 45;Aschaffenburg ROB;Goldbach;Hösbach;Sailauf;;;;;Eichenberg;;Aschaffenburg ROB - Goldbach - Hösbach - Sailauf - Eichenberg 22 | 47;Aschaffenburg ROB;Haibach;Grünmorsbach;Straßbessenbach;Bessenbach;Keilberg;Hösbach Bf.;Waldaschaff;Rothenbuch;Wochenendverkehr Hochspessart;(Wochenendverkehr Hochspessart) Aschaffenburg ROB - Haibach - Grünmorsbach - Straßbessenbach - Bessenbach - Keilberg - Hösbach Bf. - Waldaschaff - Rothenbuch 23 | 50;Aschaffenburg ROB;Mainaschaff;Kleinostheim;Karlstein;;;;;Kahl;;Aschaffenburg ROB - Mainaschaff - Kleinostheim - Karlstein - Kahl 24 | 52;Großostheim Friedenschule;Großostheim Realschule;Pflaumheim;Wenigumstadt;Ringheim;;;;Stockstadt;Schulverband Großostheim;(Schulverband Großostheim) Großostheim Friedenschule - Großostheim Realschule - Pflaumheim - Wenigumstadt - Ringheim - Stockstadt 25 | 53;Aschaffenburg ROB;;Großostheim;Ringheim;Schaafheim;;;;Babenhausen (Hess.);;Aschaffenburg ROB - Großostheim - Ringheim - Schaafheim - Babenhausen (Hess.) 26 | 54;Aschaffenburg ROB;;Großostheim;Pflaumheim;Schaafheim;;;;Babenhausen (Hess.);;Aschaffenburg ROB - Großostheim - Pflaumheim - Schaafheim - Babenhausen (Hess.) 27 | 55;Aschaffenburg ROB;;Großostheim;Mömlingen;;;;;Obernburg-Elsenfeld Bf.;;Aschaffenburg ROB - Großostheim - Mömlingen - Obernburg-Elsenfeld Bf. 28 | 56;Sulzbach am Main;Niedernberg;;;;;;;Großostheim;;Sulzbach am Main - Niedernberg - Großostheim 29 | 58;Aschaffenburg ROB;Mainaschaff;Zellhausen;Seligenstadt;;;;;Rodgau-Weiskirchen;;Aschaffenburg ROB - Mainaschaff - Zellhausen - Seligenstadt - Rodgau-Weiskirchen 30 | KEX 33;Schöllkrippen;;;;;;;;Aschaffenburg;Kahlgrundexpress;(Kahlgrundexpress) Schöllkrippen - Aschaffenburg 31 | -------------------------------------------------------------------------------- /sources/linien_vab_kreis_mb.csv: -------------------------------------------------------------------------------- 1 | Linie;Start;über 1;über 2;über 3;über 4;über 5;Ziel;Laufweg 2 | 60;Aschaffenburg ROB;Niedernberg;Obernburg;;;;Obernburg-Elsenfeld Bf.;Aschaffenburg ROB - Niedernberg - Obernburg - Obernburg-Elsenfeld Bf. 3 | 61;Aschaffenburg ROB;Sulzbach;Kleinwallstadt;Obernburg-Elsenfeld Bf.;Erlenbach;Klingenberg;Mönchberg;Aschaffenburg ROB - Sulzbach - Kleinwallstadt - Obernburg-Elsenfeld Bf. - Erlenbach - Klingenberg - Mönchberg 4 | 62A;Roßbach;Volkersbrunn;;;;;Heimbuchenthal;Roßbach - Volkersbrunn - Heimbuchenthal 5 | 62;Aschaffenburg ROB;Leidersbach;Hausen;;;;Obernburg-Elsenfeld Bf.;Aschaffenburg ROB - Leidersbach - Hausen - Obernburg-Elsenfeld Bf. 6 | 63;Aschaffenburg ROB;Gailbach;Soden;Sulzbach;;;Dornau;Aschaffenburg ROB - Gailbach - Soden - Sulzbach - Dornau 7 | 64;Obernburg-Elsenfeld Bf.;Eschau;;;;;Neuhammer;Obernburg-Elsenfeld Bf. - Eschau - Neuhammer 8 | 65;Streit;Mechenhard;;;;;Erlenbach;Streit - Mechenhard - Erlenbach 9 | 67;Klingenberg;Wörth;Haingrund;;;;Seckmauern;Klingenberg - Wörth - Haingrund - Seckmauern 10 | 68;Obernburg-Elsenfeld Bf.;Obernburg;Eisenbach;;;;Mömlingen;Obernburg-Elsenfeld Bf. - Obernburg - Eisenbach - Mömlingen 11 | 69;Elsenfeld;Eichelsbach;Eschau;Streit;Rück;;Elsenfeld;Elsenfeld - Eichelsbach - Eschau - Streit - Rück - Elsenfeld 12 | 80;Miltenberg;Großheubach;Miltenberg;Bürgstadt;;;Miltenberg;Miltenberg - Großheubach - Miltenberg - Bürgstadt - Miltenberg 13 | 81;Miltenberg;Großheubach;Röllfeld;Klingenberg;;;Erlenbach Krankenhaus;Miltenberg - Großheubach - Röllfeld - Klingenberg - Erlenbach Krankenhaus 14 | 82;Miltenberg;Eichelbühl;;;;;Eichenbühl-Höhenorte;Miltenberg - Eichelbühl - Eichenbühl-Höhenorte 15 | 83;Miltenberg;Großheubach;Mönchberg;Eschau;Altenbuch;Stadtprozelten;Wertheim;Miltenberg - Großheubach - Mönchberg - Eschau - Altenbuch - Stadtprozelten - Wertheim 16 | 84;Miltenberg;Breitendiel;Mainbullau;Weilbach;;;Amorbach;Miltenberg - Breitendiel - Mainbullau - Weilbach - Amorbach 17 | 85;Miltenberg;Bürgstadt;Freudenberg;Stadtprozelten;;;Wertheim;Miltenberg - Bürgstadt - Freudenberg - Stadtprozelten - Wertheim 18 | 86;Miltenberg;Rüdenau;Kleinheubach;;;;Laudenbach;Miltenberg - Rüdenau - Kleinheubach - Laudenbach 19 | 87;Miltenberg;Wenschdorf;Monbrunn;Schippach;;;Berndiel;Miltenberg - Wenschdorf - Monbrunn - Schippach - Berndiel 20 | 88;Breitendiel;;;;;;Miltenberg;Breitendiel - Miltenberg 21 | 92;Amorbach;;;;;;Amorbach;Amorbach - Amorbach 22 | 93;Schneeberg;Kirchzell;Amorbach;Kleinheubach;;;Obernburg - Glanzstoffwerke;Schneeberg - Kirchzell - Amorbach - Kleinheubach - Obernburg - Glanzstoffwerke 23 | 94;Amorbach;Weilbach;Weckbach;;;;Gönz;Amorbach - Weilbach - Weckbach - Gönz 24 | 95;Amorbach;Kirchzell;Watterbach;Breitenbuch;;;Ottorfszell;Amorbach - Kirchzell - Watterbach - Breitenbuch - Ottorfszell 25 | 96;Amorbach;Buch;Preunschen;Mörschenhardt;;;Mudau;Amorbach - Buch - Preunschen - Mörschenhardt - Mudau 26 | 97;Gottersdorf;Reichartshausen;Neudorf;;;;Amorbach;Gottersdorf - Reichartshausen - Neudorf - Amorbach 27 | 98;Amorbach;Boxbrunn;;;;;Beuchen;Amorbach - Boxbrunn - Beuchen 28 | 99;Amorbach;Schneeberg;Hambrunn;;;;Zittenfelden;Amorbach - Schneeberg - Hambrunn - Zittenfelden 29 | -------------------------------------------------------------------------------- /sources/linien_vab_stadt_ab.csv: -------------------------------------------------------------------------------- 1 | Linie;Start;über 1;über 2;über 3;Ziel;Laufweg 2 | 1;Aschaffenburg ROB;Obernau;;;Sulzbach;Aschaffenburg ROB - Obernau - Sulzbach 3 | 2;Aschaffenburg ROB;Dämmer Tor;;;Strietwald;Aschaffenburg ROB - Dämmer Tor - Strietwald 4 | 3;Aschaffenburg ROB;Leider;Waldfriedhof;;Stockstadt;Aschaffenburg ROB - Leider - Waldfriedhof - Stockstadt 5 | 4;Aschaffenburg ROB;;;;Schweinheim;Aschaffenburg ROB - Schweinheim 6 | 5;Aschaffenburg ROB;Gailbach;;;Dörrmorsbach;Aschaffenburg ROB - Gailbach - Dörrmorsbach 7 | 6;Aschaffenburg ROB;;;;Nilkheim;Aschaffenburg ROB - Nilkheim 8 | 7;Aschaffenburg ROB;Goldbach;Kugeldberg;;Unterafferbach;Aschaffenburg ROB - Goldbach - Kugeldberg - Unterafferbach 9 | 8;Aschaffenburg ROB;;;;Damm;Aschaffenburg ROB - Damm 10 | 9;Aschaffenburg ROB;;;;Glattbach;Aschaffenburg ROB - Glattbach 11 | 10;Aschaffenburg ROB;Schweinheim Blütenstraße;;;Schweinheim;Aschaffenburg ROB - Schweinheim Blütenstraße - Schweinheim 12 | 11;Aschaffenburg ROB;Dämmer Tor;;;Strietwald IHK;Aschaffenburg ROB - Dämmer Tor - Strietwald IHK 13 | 12;Aschaffenburg ROB;;;;Klinikum;Aschaffenburg ROB - Klinikum 14 | 14;Aschaffenburg ROB;;;;Mainaschaff;Aschaffenburg ROB - Mainaschaff 15 | 15;Aschaffenburg ROB;Bessenbacher Weg;;;Schweinheim;Aschaffenburg ROB - Bessenbacher Weg - Schweinheim 16 | 16;Aschaffenburg ROB;Klinikum;Haibach;Grünmorsbach;Dörrmorsbach;Aschaffenburg ROB - Klinikum - Haibach - Grünmorsbach - Dörrmorsbach 17 | -------------------------------------------------------------------------------- /sources/linien_wtv.csv: -------------------------------------------------------------------------------- 1 | Linie;Laufweg 2 | 950;(Bonndorf -) Ewattingen - Wutach - Mundelfingen - Döggingen 3 | 7255;(Menzenschwand -) Seebrugg - Altglashütten < Bärental / Falkau > Titisee (- Neustadt) 4 | 7257;Seebrugg - Fischbach - Lenzkirch - Titisee - Neustadt 5 | 7258;Neustadt - Lenzkirch - Gündelwangen - Bonndorf 6 | 7259;SBG Wanderbus: Löffingen - Schattenmühle - Unadingen 7 | 7300;(Titisee - Feldberg -) Todtnau - Zell i. Wiesental (- Schopfheim) 8 | 7301;(Schwörstadt -) Rheinfelden - Wyhlen - Grenzach - Lörrach/- Basel Bad Bf 9 | 7313;Schopfheim - Schwörstadt 10 | 7317;Citybus Bad Säckingen Linie 1: Lohgerbe - Obersäckingen - Bremhag - Sonnenrain - Lohgerbe 11 | 3317;Citybus Bad Säckingen Linie 2: Lohgerbe - Kurgebiet - Lohgerbe 12 | 3217;Citybus Bad Säckingen Linie 3: Lohgerbe - Weststadt - Lohgerbe 13 | 7318;Waldshut - Dogern -/Unteralpfen - Birkingen - Buch - Albbruck 14 | 7319;St. Blasien - Häusern - Seebrugg 15 | 7320;Todtmoos - Wehr - Bad Säckingen 16 | 7321;St. Blasien - Menzenschwand - Bernau - St. Blasien/- Todtmoos 17 | 3321;Todtmoos - Herrenschwand - Präg - Todtnau 18 | 3221;St. Blasien - Ibach - Todtmoos 19 | 7322;Waldshut - Waldkirch - Höchenschwand - Häusern - St. Blasien 20 | 3322;Waldshut - Eschbach - Gaiss - Unteralpfen 21 | 3222;Höchenschwand - Attlisberg - Frohnschwand - Tiefenhäusern 22 | 7323;St. Blasien - Schlageten - Niedermühle 23 | 7324;St. Blasien - Ibach - Dachsberg - Rotzingen - Görwihl (- Albbruck - Waldshut) 24 | 3324;Görwihl - Niederwihl - Tiefenstein - Schachen - Albbruck (- Waldshut) 25 | 7325;StadtBus Laufenburg Linie 1: Ostbahnhof - Binzgen - Hochsal - Rotzel 26 | 3225;StadtBus Laufenburg Linie 2: Ostbahnhof - Grunholz - Luttingen - Stadenhausen - Ostbahnhof 27 | 7326;Bad Säckingen - Murg - Niederhof - Hänner (- Rickenbach) 28 | 7327;(Bad Säckingen -) Rickenbach - Görwihl - Herrischried - Rotzingen 29 | 7328;Bad Säckingen - Rickenbach - Herrischried - Todtmoos 30 | 7329;Wehr - Rickenbach - Hütten - Herrischried 31 | 7330;Laufenburg - Binzgen - Niederhof - Hänner 32 | 7331;(Waldshut -) Lauchringen - Griessen < Weisweil / Rechberg > Erzingen 33 | 7334;Waldshut - Albbruck - Laufenburg - Murg - Bad Säckingen 34 | 7335;Regiobus Bad Säckingen - Brennet - Wehr - Hasel - Schopfheim 35 | 3335;Brennet -/Hölzle - Enkendorf - Wehr - Zelg 36 | 7336;Waldshut Westring Busbahnhof - Krankenhaus - Liedermatte - Chilbiplatz - Busbahnhof 37 | 7337;Waldshut Nordring Busbahnhof - Chilbiplatz - Schmitzingen - Busbahnhof 38 | 7338;Waldshut - Lauchringen - Eggingen - Stühlingen - Fützen 39 | 7339;Waldshut Ostring Busbahnhof - Chilbiplatz - Aarberg - Busbahnhof 40 | 7340;Waldshut -/Tiengen - Kadelburg - Dangstetten - Reckingen - Hohentengen 41 | 3340;Lienheim - Bergöschingen - Hohentengen - Stetten 42 | 7341;Waldshut -/Tiengen - Gurtweil - Nöggenschwiel/- Indlekofen - Brunnardern 43 | 7342;Waldshut -/Tiengen - Gurtweil - (Berau -) Ühlingen - Grafenhausen - Seebrugg 44 | 3342;Ühlingen - Riedern am Wald (- Berau) -/Schönenbach - Buggenried - Mettenberg - Grafenhausen 45 | 3242;Waldshut -/Tiengen - Gurtweil - Berau - Brenden - Schönenbach 46 | 3142;Tiengen - Detzeln - Untermettingen - Ühlingen 47 | 7343;Bonndorf - Gündelwangen/- Grafenhausen - Seebrugg 48 | 7344;Bonndorf - Boll/- Lembach - Ewattingen (- Wutach Wutachmühle) 49 | 3344;Wanderbus Wutachschlucht: Holzschlag - Bonndorf - Schattenmühle - Bonndorf - Ewattingen - Wutach 50 | 7345;Waldshut - Tiengen - Detzeln - Untermettingen - Bettmaringen - Wellendingen - Bonndorf 51 | 3345;Tiengen - Detzeln - Aichen - Gurtweil - Tiengen 52 | 7346;Stühlingen - Weizen < Oberwangen / Dillendorf > Wellendingen - Bonndorf 53 | 3346;Stühlingen - Weizen - Grimmelshofen - Lausheim 54 | 3246;Stühlingen < Untere Alp / Eggingen > - < Bettmaringen / Wittlekofen > Bonndorf 55 | 7347;Erzingen - Grießen - Dettighofen - Jestetten 56 | 3347;Jestetten - Lottstetten - Nack 57 | 3247;Jestetten Altenburg - Jestetten 58 | 7348;Stadtverkehr Tiengen: Schulzentrum - Bahnhof - Mittlerer Berg - Stadtmitte - Schulzentrum 59 | 9051;FreizeitBus Menzenschwand - Äulemer Kreuz - Aha - Schluchsee 60 | -------------------------------------------------------------------------------- /sources/orte_ch_add.csv: -------------------------------------------------------------------------------- 1 | Abk;Name 2 | DO2;Domodossola 2 3 | DOFM;Domodossola Güterumfahrung 4 | DOFS;Domodossola Personenbahnhof 5 | KODB;Konstanz DB 6 | LIFS;Luino 7 | LBT;Lötschberg Basistunnel 8 | NBS;Neubaustrecke (Bahn 2000) 9 | -------------------------------------------------------------------------------- /sources/orte_de_add.csv: -------------------------------------------------------------------------------- 1 | Abk;Name 2 | XFRPV;Prény (Abzw) 3 | WPRAF;Prora Fundgrube/L29 (seit 2021/10 nicht mehr offizielle Abkürzung) 4 | FAOH;Frankfurt Abzweig Osthafen;Aus der Machbarkeitsstudie Fernbahntunnel Frankfurt 5 | FFTS;Frankfurt Hauptbahnhof Tief Süd;Aus der Machbarkeitsstudie Fernbahntunnel Frankfurt 6 | FHFG B;Hofgeismar Brücke 7 | FHFGB;Hofgeismar Bus 8 | -------------------------------------------------------------------------------- /sources/orte_hamburg.csv: -------------------------------------------------------------------------------- 1 | Name;Abk 2 | Ahrensburg-Ost;AO 3 | Ahrensburg-West;AW 4 | Alsterdorf;AL 5 | Alter Teichweg;AT 6 | Barmbek;BA 7 | Baumwall;BL 8 | Beimoor;BM 9 | Berliner Tor;BT 10 | Berne;BE 11 | Billstedt;BI 12 | Borgweg;BO 13 | Brückenstraße;BK 14 | Buchenkamp;BP 15 | Buckhorn;BN 16 | Burgstraße;BG 17 | Christuskirche;CH 18 | Dehnhaide;DE 19 | Emilienstraße;EM 20 | Eppendorfer Baum;EP 21 | Farmsen;FA 22 | Feldstraße;FE 23 | Fuhlsbüttel;FU 24 | Fuhlsbüttel-Nord;FL 25 | Gänsemarkt;GM 26 | Garstedt;GA 27 | Großhansdorf;GH 28 | Habichtstraße;HA 29 | Hagenbecks Tierpark;HG 30 | Hagendeel;HL 31 | Hallerstraße;HR 32 | Hamburger Straße;HS 33 | Hammer Kirche;HK 34 | Hauptbahnhof-Nord;HX 35 | Hauptbahnhof-Süd;HB 36 | Hellkamp;HE 37 | Hoheluftbrücke;HO 38 | Hoisbüttel;HT 39 | Horner Rennbahn;HN 40 | Hudtwalckerstraße;HU 41 | Joachim-Mähl-Straße;JM 42 | Jungfernstieg;JG 43 | Kellinghusenstraße;KE 44 | Kiekut;KI 45 | Kiwittsmoor;KM 46 | Klein Borstel;KB 47 | Klosterstern;KR 48 | Landungsbrücken;LB 49 | Langenhorn-Markt;LM 50 | Langenhorn-Nord;LN 51 | Lattenkamp;LA 52 | Legienstraße;LE 53 | Lohmühlenstraße;LS 54 | Lübecker Straße;LU 55 | Lutterothstraße;LT 56 | Meiendorfer Weg;ME 57 | Merkenstraße;MS 58 | Meßberg;MB 59 | Messehallen;MH 60 | Mönckebergstraße;MO 61 | Mümmelmannsberg;MG 62 | Mundsburg;MU 63 | Niendorf-Markt;NM 64 | Niendorf-Nord;NN 65 | Norderstedt-Mitte;NO 66 | Ochsenzoll;OZ 67 | Ohlsdorf;OH 68 | Ohlstedt;OT 69 | Osterstraße;OS 70 | Rathaus;RA 71 | Rauhes Haus;RH 72 | Richtweg;RW 73 | Ritterstraße;RI 74 | Rödingsmarkt;RD 75 | Rothenburgsort;RO 76 | Saarlandstraße;SA 77 | Schippelsweg;SW 78 | Schlump;SL 79 | Schmalenbek;SK 80 | Sengelmannstraße;SE 81 | Sierichstraße;SI 82 | Spaldingstraße;SP 83 | St. Pauli;PA 84 | Steinfurther Allee;SF 85 | Steinstraße;ST 86 | Sternschanze;SZ 87 | Straßburger Straße;SR 88 | Süderstraße;SS 89 | Trabrennbahn;TR 90 | Uhlandstraße;UH 91 | Volksdorf;VF 92 | Wandsbeker Chaussee;WR 93 | Wandsbek-Gartenstadt;WK 94 | Wandsbek-Markt;WM 95 | Wartenau;WA 96 | -------------------------------------------------------------------------------- /sources/orte_wien.csv: -------------------------------------------------------------------------------- 1 | Abk;Name 2 | AK;Aderklaaer Straße 3 | AS;Alser Straße 4 | AE;Alt Erlaa 5 | AD;Alte Donau 6 | PW;Am Schöpfwerk (U) 7 | AP;Aspern (U2) 8 | BA;Babenbergerstraße - Museumsquartier 9 | BR;Braunschweiggasse 10 | BE;Breitensee 11 | BS;Brünner Straße (in F) 12 | BM;Brunn-Maria Enzersdorf 13 | BU;Burggasse 14 | DJ;Donauinsel 15 | DP;Donauspital (U2) 16 | DT;Donaustadtbrücke (U2) 17 | DS;Dresdner Straße 18 | EK;Enkplatz 19 | ED;Erdberg 20 | ER;Erlaaer Straße 21 | FL;Floridsdorf (in F) 22 | FB;Friedensbrücke 23 | AW;Gasometer 24 | GE;Geiselbergstraße (in Sah) 25 | GH;Gersthof 26 | SM;Grillgasse (in Zur) 27 | GF;Großfeldsieldung 28 | GU;Gumpendorfer Straße 29 | GG;Gutheil-Schoder-Gasse 30 | HK;Handelskai 31 | HK;Handelskai (in Wv) 32 | AR;Hardegggasse (U2) 33 | HN;Hernals 34 | HZ;Herrengasse 35 | HI;Hietzing 36 | HH;Hütteldorfer Straße 37 | IP;Inzersdorf - Personenbahnhof 38 | JG;Jägerstraße 39 | J;Jedlersdorf (in F) 40 | JO;Johnstraße 41 | JS;Josefstädter Straße 42 | KT;Kagraner Platz 43 | KM;Kaisermühlen - Vienna International Centre 44 | KN;Kardinal-Nagl-Platz 45 | KP;Karlsplatz 46 | KR;Kendlerstraße 47 | KE;Keplerplatz 48 | KG;Kettenbrückengasse 49 | KL;Kledering (in Zur) 50 | KS;Klein Schwechat (in Kls) 51 | KI;Krieau 52 | KB;Krottenbachstraße 53 | LE;Längenfeldgasse 54 | LP;Leopoldau 55 | LU;Leopoldau U 56 | LG;Liesing (in Lg) 57 | LB;Lobau 58 | MG;Margaretengürtel 59 | MH;Meidling Hauptstraße 60 | MS;Messe 61 | MB;Michelbeuern, AKH 62 | NP;Nestroyplatz 63 | NE;Neu Erlaa 64 | MA;Neubaugasse - Mariahilfer Straße 65 | ND;Neue Donau 66 | NH;Niederhofstraße 67 | NF;Nußdorf 68 | NS;Nussdorfer Straße 69 | OV;Ober St. Veit 70 | OD;Oberdöbling 71 | OP;Oper (WLB) 72 | OK;Ottakring U 73 | PZ;Penzing 74 | PE;Perfektastraße 75 | PH;Philadelphiabrücke 76 | PG;Pilgramgasse 77 | SU;Praterkai 78 | PS;Purkersdorf Sanatorium 79 | RH;Rathaus 80 | RB;Rennbahnweg 81 | RW;Rennweg (in Nw) 82 | RP;Reumannplatz 83 | RG;Rochusgasse 84 | RL;Rossauer Lände 85 | SG;Schlachthausgasse 86 | SB;Schönbrunn 87 | LL;Schönbrunner Allee 88 | SK;Schöpfwerk (WLB) 89 | SR;Schottenring 90 | SO;Schottentor 91 | GS;Schwechat (in Kls) 92 | SP;Schwedenplatz 93 | SH;Schweglerstraße 94 | TH;Siebenhirten 95 | SI;Siemensstraße (in F) 96 | SA;Simmering (in El) 97 | AU;Spittelau (in Wf) 98 | SW;Stadion 99 | SD;Stadlau (in St) 100 | ST;Stadtpark 101 | SZ;Stephansplatz 102 | SE;Stubentor 103 | TB;Taborstraße 104 | TA;Taubstummengasse 105 | TS;Thaliastraße 106 | TG;Traisengasse 107 | TE;Tscherttegasse 108 | UV;Unter St. Veit 109 | MS;Vienna Biocenter-St.Marx 110 | VT;Volkstheater 111 | VS;Vorgartenstraße 112 | VD;Vösendorf - Siebenhirten 113 | WA;Währinger Straße, Volksoper 114 | AM;Wien Atzgersdorf (in Lg) 115 | KA;Wien Breitenleer Straße 116 | SV;Wien Erzherzog Karl-Straße (in St) 117 | WF;Wien Franz-Josefs-Bf (in Wf) 118 | WU;Wien Hadersdorf 119 | HD;Wien Haidestraße (in El) 120 | HU;Wien Hausfeldstraße 121 | HS;Wien Heiligenstadt 122 | HE;Wien Hetzendorf (in Mat) 123 | HA;Wien Hirschstetten (in St) 124 | HF;Wien Hütteldorf 125 | MP;Wien Matzleinsdorfer Platz (in Mat) 126 | MI;Wien Meidling (in Mat) 127 | LA;Wien Mitte (in Nw) 128 | PR;Wien Praterstern (in Nw) 129 | S;Wien Speising 130 | SS;Wien Strebersdorf (in F) 131 | WB;Wien Südbahnhof (in Wsp) 132 | WB;Wien Südbf (in Wbo) 133 | WB;Wien Südbf (in Wbo) 134 | SL;Wien Südtiroler Platz (in Wsp) 135 | SN;Wien Süßenbrunn (in Sue) 136 | WW;Wien Weidlingau 137 | WS;Wien Westbf (in Ws) 138 | WG;Wolfganggasse 139 | ZF;Zentralfriedhof 140 | GZ;Zieglergasse 141 | PP;Zippererstraße 142 | -------------------------------------------------------------------------------- /sources/signale_bostrab.csv: -------------------------------------------------------------------------------- 1 | Abk;1976;1987;Bedeutung 2 | H0;;;Halt 3 | H1;;;Fahrt 4 | H2;;;Fahrt mit Geschwindigkeitsbeschränkung 5 | V0;;;Am folgenden Hauptsignal ist Halt zu erwarten 6 | V1;;;Am folgenden Hauptsignal ist Fahrt zu erwarten 7 | V2;;;Am folgenden Hauptsignal ist Fahrt mit Geschwindigkeitsbeschränkung zu erwarten 8 | F0;;;Halt 9 | F1;;;Fahrt freigegeben nur geradeaus 10 | F2;;;Fahrt freigegeben nur nach rechts 11 | F3;;;Fahrt freigegeben nur nach links 12 | F4;;;Halt zu erwarten 13 | F5;;;Fahrt freigegeben unter Beachtung der Abbiegeregeln nach §9 Straßenverkehrs-Ordnung 14 | A1;;;Türen schließen 15 | A2;;;Abfahren 16 | A2a;;;Abfahren (Optisch oder akustisch durch das Zugpersonal) 17 | A2b;;;Abfahren (Lichtsignal) 18 | Z1;;;Spitzensignal 19 | Z2;;;Schlusssignal 20 | Z3;;;Bremssignal 21 | Z4;;;Fahrtrichtungssignal 22 | Z5;;;Warnblinksignal 23 | G1;;;Ankündigung der Geschwindigkeitsbeschränkung. Zeigt Änderungen der zulässigen Geschwindigkeit nach unten an. 24 | G1a;;;Ankündigung der Geschwindigkeitsbeschränkung. Zeigt Änderungen der zulässigen Geschwindigkeit nach unten an. (Signaltafel) 25 | G1b;;;Ankündigung der Geschwindigkeitsbeschränkung. Zeigt Änderungen der zulässigen Geschwindigkeit nach unten an. (Lichtsignal) 26 | G2;;;Beginn der Geschwindigkeitsbeschränkung. Zeigt Änderungen der zulässigen Geschwindigkeit nach unten an. 27 | G2a;;;Beginn der Geschwindigkeitsbeschränkung. Zeigt Änderungen der zulässigen Geschwindigkeit nach unten an. (Signaltafel) 28 | G2b;;;Beginn der Geschwindigkeitsbeschränkung. Zeigt Änderungen der zulässigen Geschwindigkeit nach unten an. (Lichtsignal) 29 | G3;;;Ende der Geschwindigkeitsbeschränkung 30 | G4;;;Beginn der Geschwindigkeitsbegrenzung. Zeigt Änderungen der zulässigen Geschwindigkeit nach oben an. 31 | Sh1;;;Zwangshalt. Kennzeichnet Stellen, an denen bei Fahrt auf Sicht in jedem Fall anzuhalten ist. 32 | Sh2;;;Schutzhalt. Weiterfahrt ist unzulässig. 33 | Sh3;;;Nothalt 34 | Sh3a;;;Nothalt (akustisch) 35 | Sh3b;;;Nothalt (Kreissignal, Tagzeichen) 36 | Sh3c;;;Nothalt (Kreissignal, Nachtzeichen) 37 | Sh3d;;;Nothalt (Lichtsignal) 38 | Sh4;;;Läuten 39 | Sh5;;;Achtung 40 | Sh6;;;Grenzzeichen 41 | Sh7;;;Haltetafel 42 | R1;;;Wegfahren 43 | R2;;;Herkommen 44 | R3;;;Rangierhalt 45 | St1;;;Signalkontakt 46 | St2;;;Weichenkontakt 47 | St3;;;Ausschalten 48 | St4;;;Einschalten erlaubt 49 | St5;;;Stromabnehmer abziehen 50 | St6;;;Stromabnehmer anlegen 51 | St7;;;Streckentrenner. Der Fahrstrom ist kurz auszuschalten. 52 | St8;;;Halt für Fahrzeuge mit angelegtem Stromabnehmer 53 | W1;;;Weiche steht für Fahrt geradeaus mit höchstens 15 km/h 54 | W2;;;Weiche steht für Fahrt nach rechts mit höchstens 15 km/h 55 | W3;;;Weiche steht für Fahrt nach links mit höchstens 15 km/h 56 | W11;;;Weiche steht für Fahrt geradeaus mit zulässiger Geschwindigkeit 57 | W12;;;Weiche steht für Fahrt nach rechts mit zulässiger Geschwindigkeit 58 | W13;;;Weiche steht für Fahrt nach links mit zulässiger Geschwindigkeit 59 | W14;;;Weiche darf nicht aufgefahren werden 60 | Bü0;;;"Halt vor dem Bahnübergang; Weiterfahrt nur, wenn es die Verkehrslage erlaubt" 61 | Bü1;;;Der Bahnübergang darf befahren werden 62 | Bü2;;;Ein Überwachungssignal ist zu erwarten 63 | So1;;;Beginn einer Strecke mit Zugsicherung 64 | So2;;;Ende einer Strecke mit Zugsicherung 65 | So3;;;Standortkennzeichen. Gibt den Standort von Hauptsignalen an. 66 | So4;;;Auftragsschild. Gibt den Auftrag, am Signal H0 unter Beachtung besonderer Anordnungen, die in einer Dienstanweisung festgelegt sind, vorbeizufahren. 67 | So5;;;Begegnungsverbot – Anfang – 68 | So6;;;Begegnungsverbot – Ende – 69 | -------------------------------------------------------------------------------- /sources/signale_no.csv: -------------------------------------------------------------------------------- 1 | Nummer;Betydning 2 | 1;Stopp 3 | 2;Varsom 4 | 3,3a;Innkjør 5 | 3b;Passér 6 | 4;Klar linje 7 | 5;Avgang 8 | 6;Tilsett bremsen 9 | 7;Løs bremsen 10 | 8;Kjør fram forbi middel 11 | 9;Kryssende tog er kommet 12 | 20,20a,20b;Stopp 13 | 21;Kjør (avvikelse over en eller flere sporveksler) 14 | 22;Kjør (uten avvikende togveksel) 15 | 23;Stopp (Forsignal) 16 | 24;Kjør (avvikelse over en eller flere sporveksler) (Forsignal) 17 | 25;Kjør (uten avvikende togveksel) (Forsignal) 18 | 31;Kjør (Hjelpesignal) 19 | 32;Vanskelig Togvei (Hjelpesignal) 20 | 34;Spornummersignal 21 | 35;Hovedlinjesignal 22 | 36;Togsportsignal 23 | 36a;Togsportsignal (fra avviketogspor) 24 | 36b;Togsportsignal (fra hovedtogspor) 25 | 37;Tillset bremsen 26 | 38;Løs bremsen 27 | 39;Avgang 28 | 41,43;Skifting forbudt 29 | 42,45;Skifting tillatt 30 | 44;Varsom skifting tillatt 31 | 46;Frigitt for lokal skifting 32 | 48;Stopp 33 | 49;Kjør fram 34 | 50;Bakk 35 | 51a;Rett fram 36 | 51b;Fra avvikespor 37 | 51c;Til venstre 38 | 51d;Til høyre 39 | 52a;Fra venstre til høyre 40 | 52b;Fra høyre til venstre 41 | 52c;Fra venstre til venstre 42 | 52d;Fra høyre til høyre 43 | 53;Sporet sperret 44 | 54;Sporet fritt 45 | 55;Stopp foran planovergangen 46 | 56;Planovergangen kan passeres 47 | 57;Planovergangsignalet viser "Stopp foran planovergangen" 48 | 58;Planovergangsignalet viser "Planovergangen kan passeres" 49 | 59;Rasfare 50 | 60;Tog kan passere rasfarestrekning 51 | 65a;Jordet seksjon 52 | 65b;Varslesignal for signal 65c og 65e 53 | 65c;Utkopling foran død seksjon 54 | 65d;Innkoppling etter død seksjon 55 | 65e;Senking av strømavtaker 56 | 65f;Heving av strømavtaker 57 | 65g;Stopp for elektrisk lokomotiv 58 | 66;Togvei slutt 59 | 67;Orienteringsignal 60 | 67a;Orienteringsignal 61 | 67b;Orienteringsignal for planovergang 62 | 67c;Orienteringsignal for holdeplass 63 | 67d;Orienteringsignal for planovergang og holdeplass 64 | 68;Hastighetssignaler 65 | 68a;Nedsatt kjørehastighet 66 | 68b;Økt kjørehastighet 67 | 68c;Avvikende nedsatt kjørehastighet 68 | 68d;Avvikende økt kjørehastighet 69 | 69;Midlertidig hastighetsignal 70 | 69a;Midlertidig kjørehastighet 71 | 69b;Midlertidig hastighet opphører 72 | 76;Stopp 73 | 78;Ingen avstigning 74 | 80;Gi akt og bemerket 75 | 81;Bremser på 76 | 82;Stopp 77 | 83;Tog kommer 78 | 84;Bremser av 79 | 85;Beredt 80 | 86;Alarm, faresignal 81 | 87;Kjøretillatelse mottatt 82 | 90;Forlamper 83 | 91;Baklamper 84 | 95;Sluttsignal 85 | -------------------------------------------------------------------------------- /sources/strecken_de_alt.csv: -------------------------------------------------------------------------------- 1 | Tabellennummer;Liniennummer;Laufweg 2 | 136;RB 65 (neg);Niebüll - Dagebüll Mole 3 | 136 1,136¹;RB 66 (neg);Niebüll - Tønder (DK) 4 | 137;A 1 (AKN);Neumünster - Kaltenkrchen - Hamburg-Eidelstedt 5 | 138;A 2 (AKN);Ulzburg Süd - Norderstedt Mitte 6 | 139;A 3 (AKN);Elmshorn - Barmstedt - Ulzburg Süd 7 | 173;RB 16;Neustrelitz - Mirow 8 | 174;RB 19;Parchim - Karow (Meckl) - Plau am See 9 | 204;RE 4;Stendal - Berlin 10 | 209 46,209⁴⁶;RB 46;Cottbus - Forst (Lausitz) 11 | 209 51,209⁵¹;RB 51;Rathenow - Brandenburg 12 | 209 54,209⁵⁴;RB 54;Löwenberg (Mark) - Rheinsberg (Mark) 13 | 209 63,209⁶³;RB 63;Eberswalde - Joachimsthal - Templin Stadt 14 | 209 73,209⁷³;RB 73;Neustadt (Dosse) - Pritzwalk 15 | 209 74,209⁷⁴;RB 74;Pritzwalk - Meyenburg 16 | 220;RB 65;Cottbus - Görlitz - Zittau 17 | 229;RB 64;Görlitz - Hoyerswerda 18 | 236;L 7;Seifhennersdorf - Zittau 19 | 238;SOEG;Zittau - Kurort Oybin/Kurort Jonsdorf 20 | 269;RB 33;Stendal - Tangermünde 21 | 396;;Emden - Emden Außenhafen 22 | 479;RB 37;Boppard - Emmelshausen 23 | 514;RB 83 (FEG);Freiberg (Sachs) - Holzhau 24 | 691;RB 85;Bullay - Traben-Trarbach 25 | -------------------------------------------------------------------------------- /sources/strecken_de_namen.csv: -------------------------------------------------------------------------------- 1 | Abk;Beschreibung;Nummer 2 | BBT;Brenner Basistunnel; 3 | GBT;Gotthard Basistunnel; 4 | KRM;SFS Köln-Rhein/Main;$2690 5 | LBT;Lötschberg Basistunnel; 6 | NBS;Neubaustrecke; 7 | SFS;Schnellfahrstrecke; 8 | VDE;Verkehrsprojekte Deutsche Einheit; 9 | VDE1;Lübeck/Hagenow Land-Rostock-Stralsund; 10 | VDE2;Berlin-Hamburg;$6100 11 | VDE3;Stendal-Uelzen;$6899 12 | VDE4;SFS Hannover-Berlin;$6107 13 | VDE5;Helmstedt-Magdeburg-Berlin;$6400 14 | VDE6;Halle-Hann. Münden; 15 | VDE7;Bebra-Erfurt; 16 | VDE8;Berlin-Nürnberg; 17 | VDE8¹,VDE8 1;Nürnberg-Erfurt; 18 | VDE8²,VDE8 2;Erfurt-Leipzig/Halle; 19 | VDE8³,VDE8 3;Leipzig/Halle-Berlin; 20 | VDE9;Leipzig-Dresden; 21 | 3669;Frankfurt Abzweig Osthafen - Frankfurt Mainkur (Fernbahntunnel Nordanbindung);Aus der Machbarkeitsstudie Fernbahntunnel Frankfurt 22 | FBTF;Fernbahntunnel Frankfurt 23 | -------------------------------------------------------------------------------- /sources/strecken_ffm.csv: -------------------------------------------------------------------------------- 1 | Abk;Weg 2 | A;A-Strecke: Südbahnhof - Heddernheim - (Ginnheim/Bad Homburg/Oberursel) 3 | Aⅰ,AⅠ,AI;A-Strecke Teilabschnitt 1 Humser Straße - Hauptwache 4 | Aⅱ,AⅡ,AII;A-Strecke Teilabschnitt 2 Hauptwache - Willy-Brandt-Platz 5 | Aⅲ,AⅢ,AIII;A-Strecke Teilabschnitt 3 Humser Straße - Weißer Stein 6 | Aⅳ,AⅣ,AIV;A-Strecke Teilabschnitt 4 Willy-Brandt-Platz - Südbahnhof 7 | Aⅴ,AⅤ,AV;A-Strecke Teilabschnitt 5 Südbahnhof - Sachsenhäuser Warte (- Neu-Isenburg) (nicht weiterverfolgt) 8 | A1;Anschlussstrecke A1: Weißer Stein - Ginnheim 9 | A2;Anschlussstrecke A2: Heddernheim - Bad Homburg Gonzenheim 10 | A3;Anschlussstrecke A3: Abzweig Nordwest - Oberursel Hohemark 11 | B;B-Strecke: Europaviertel - Hauptbahnhof - Konstablerwache - (Preungesheim/Seckbacher Landstraße) 12 | Bⅰ,BⅠ,BI;B-Strecke Teilabschnitt 1: Scheffeleck - Willy-Brandt-Platz 13 | Bⅱ,BⅡ,BII;B-Strecke Teilabschnitt 2: Willy-Brandt-Platz - Hauptbahnhof 14 | Bⅳ,BⅣ,BIV;B-Strecke Teilabschnitt 4: Hauptbahnhof - Europaviertel (ursprünglich Nied - Höchst) 15 | B1;Anschlussstrecke B1: Scheffeleck - Preungesheim 16 | B2;Anschlussstrecke B2: Konstablerwache - Seckbacher Landstraße 17 | C;C-Strecke: (Praunheim/Hausen) - Industriehof - Zoo - (Ostbahnhof / Enkheim) 18 | Cⅰ,CⅠ,CI;C-Strecke Teilabschnitt 1: Alte Oper - Hauptwache 19 | Cⅱ,CⅡ,CII;C-Strecke Teilabschnitt 2: Hauptwache - Zoo 20 | Cⅲ,CⅢ,CIII;C-Strecke Teilabschnitt 3: Bockenheimer Warte - Alte Oper 21 | Cⅳ,CⅣ,CIV;C-Strecke Teilabschnitt 4: Zoo - Ostbahnhof 22 | Cⅴ,CⅤ,CV;C-Strecke Teilabschnitt 5: Industriehof - Bockenheimer Warte 23 | C1;Anschlussstrecke C1: Zoo - Eissporhalle - Enkheim 24 | D;D-Strecke: (Schwanheim / Stadion) - Hauptbahnhof - Ginnheim - Kalbach 25 | Dⅰ,DⅠ,DI;D-Strecke Teilabschnitt 1: Hauptbahnhof - Bockenheimer Warte 26 | Dⅱ,DⅡ,DII;D-Strecke Teilabschnitt 2: Bockenheimer Warte - Ginnheim (geplant) 27 | Dⅲ,DⅢ,DIII;D-Strecke Teilabschnitt 3: Niederrad - Hauptbahnhof (nicht weiterverfolgt) 28 | Dⅳ,DⅣ,DIV;D-Strecke Teilabschnitt 4: Nordweststadt - Riedberg - Kalbach 29 | -------------------------------------------------------------------------------- /sources/strecken_no.csv: -------------------------------------------------------------------------------- 1 | Blad;Vei 2 | 1;(Skøyen - Oslo S -) Lillestrøm - Årnes - Skarnes - Kongsvinger - Charlottenberg (- Arvika) 3 | 2;Kongsvinger - Elverum 4 | 3;Skøyen - Oslo S - Loenga - Kolbotn - Ski - Moss - Fredrikstad - Sarpsborg - Halden - Kornsjø (- Ed) 5 | 4;(Skøyen - Oslo S - Kolbotn Ski -) Mysen - Sarpsborg 6 | 5;Gjøvikbanen: (Skøyen -) Oslo S - Grefsen - Hakadal - Roa - Jaren - Eina - Gjøvik 7 | 6;Flåmsbana: Myrdal - Flåm 8 | 7;Bergensbanen: Hønefoss - Ål - Myrdal - Voss - Bergen 9 | 8;Drammenbanen: Oslo S - Skøyen - Lysaker - Sandvika - Asker - Drammen 10 | 8b;Lysaker - Sandvika - Asker - Spikkestad 11 | 8c;Filipstad - Skøyen 12 | 9;Hovedbanen: Skøyen - Oslo S - Grorud - Lillestrøm - Eidsvoll 13 | 9b;Grorud - Alnabru 14 | 9c;Aker - Alnabru 15 | 9d;Alnabru - Brobekk 16 | 9e;Alnabru - Loenga 17 | 10;Eidsvoll - Hamar - Lillehammer - Ringebu - Otta - Dombås 18 | 11;Trondheim S - Trondheim M - Støren - Oppdal - Dombås 19 | 12;Raumabana: Åndalsnes - Dombås 20 | 13;Rørosbana: Støren - Røros - Tynset - Koppang - Elverum - Hamar 21 | 14;Grong - Steinkjer - Levanger - Stjørdal - Hell - Trondheim S 22 | 14b;Leangen - Trondheim M 23 | 14c;Meråkerbanen: Hell - Storlien 24 | 15;Nordlandsbanen: Bodø - Fauske - Mo i Rana - Mosjøen - Grong 25 | 16;Ofotbanen; Narvik - Bjørnfjell - Vassijaure 26 | 17;Drammen - Hokksund - Kongsberg - Hjuksebø - Nordagutu - Neslandsvatn 27 | 18;Nordagutu - Neslandsvatn - Nelaug - Kristiansand 28 | 19;Kristiansand - Sira - Egersund - Vigrestad - Bryne - Sandnes - Stavanger 29 | 20;Hokksund - Hønefoss 30 | 21;Drammen - Tønsberg - Sandefjord - Larvik - Porsgrunn - Skien - Nordagutu 31 | 21b;Tinnoset - Notodden - Hjuksebø 32 | 21c;Myrane - Ørvik - Brevik s.sp 33 | 22;Arendal - Nelaug 34 | 99;Gardermobanen: Eisdvoll - Gardermoen - Lillestrøm - Oslo S 35 | -------------------------------------------------------------------------------- /statistics: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | '''Helper for tooting bot statistics''' 4 | 5 | import argparse 6 | import configparser 7 | import datetime 8 | import logging 9 | from Externals import setup_database, set_arguments, setup_network, test_network_arguments 10 | import Persistence 11 | 12 | logger = Persistence.init_logger('statistics') 13 | 14 | one_day = datetime.timedelta(days=1) 15 | today = datetime.date.today() 16 | first = today.replace(day=1) 17 | january_first = first.replace(month=1) 18 | 19 | parser = argparse.ArgumentParser(description=__doc__) 20 | set_arguments(parser) 21 | Persistence.set_logging_args(parser, 'INFO') 22 | parser.add_argument('--since', 23 | dest='since', 24 | help='Time frame for statistics', 25 | required=False, 26 | choices=['0', 'month', 'year'], 27 | default=0) 28 | args = parser.parse_args() 29 | configuration = configparser.ConfigParser() 30 | configuration.read(args.config) 31 | args.config = configuration 32 | network = test_network_arguments(args) 33 | logger.setLevel(getattr(logging, args.log_level)) 34 | logger.debug("statistics running args: %s", args) 35 | 36 | if args.since == "0": 37 | since = 0 38 | since_text = 'Beginn' 39 | elif args.since == 'month': 40 | since = (first - one_day).strftime("%Y%m01") 41 | since_text = 'Anfang letzten Monats' 42 | elif args.since == 'year': 43 | since = (january_first - one_day).strftime("%Y0101") 44 | since_text = 'Anfang letzten Jahres' 45 | 46 | sql = setup_database(args, network) 47 | 48 | counts = sql.count_status(since=since) 49 | text = f"""Zeit für Statistik! Daten für die Zeit seit {since_text}. Ich habe: 50 | • {counts.get('found', 0)} Kürzel beantwortet, für 51 | • {counts.get('notfound', 0)} Kürzel keine lange Version erkannt und 52 | • {counts.get('blacklist', 0)} nicht beantwortet, weil sie auf der Blacklist standen. 53 | 54 | Die populärsten Kürzel waren: 55 | """ 56 | 57 | for row in sql.popular_abbrs(since): 58 | text += f"• {row['S']} ({row['C']}×)\n\u200b" 59 | text += """ 60 | Die populärsten Quellen waren: 61 | """ 62 | for row in sql.popular_sources(since): 63 | text += f"• {row['S']} ({row['C']}×)\n\u200b" 64 | text += """ 65 | (Keine Garantie für richtiges Zählen)""" 66 | 67 | print(text.replace("\u200b", "")) 68 | 69 | if args.readwrite: 70 | nw_api = setup_network(network, args, {}) 71 | nw_api.post(text) 72 | else: 73 | logger.info("Readonly: not really sending that status.") 74 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """Tester for the bot""" 4 | 5 | import argparse 6 | import logging 7 | 8 | from AnswerMachine import handle_list 9 | from Externals import setup_database 10 | import Mock 11 | 12 | import Persistence 13 | 14 | logger = Persistence.init_logger('bot') 15 | 16 | def arguments(): 17 | parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, 18 | description=__doc__, 19 | epilog="""Possible sources: 20 | testcases: Use the list of all mocked testcase tweets 21 | id: Out of the list of all mocked testcase tweets, use only the ones given with the --id 22 | attribute 23 | external: Use mocked tweet objects from tweet_details.py. That file may be created with 24 | get_tweet. 25 | """) 26 | Persistence.set_logging_args(parser) 27 | parser.add_argument('--source', 28 | choices=['testcases', 'id', 'external'], 29 | default='testcases', 30 | help='''Specify source of testable tweets. See possible values below''', 31 | required=False, 32 | ) 33 | parser.add_argument('--id', 34 | required=False, 35 | nargs='*', 36 | help='''The ids of test tweets to look at. Only valid with --source id''' 37 | ) 38 | parser.add_argument('--output', 39 | choices=['summary', 'descriptive'], 40 | default='descriptive', 41 | help='Kind of statistics output: One-line-summary or a little more verbose.' 42 | ) 43 | parser.add_argument('--readwrite', default=False, help='Will be forced to be false') 44 | args_ = parser.parse_args() 45 | args_.readwrite = False 46 | if args_.source == 'id' and args_.id is None: 47 | parser.print_help() 48 | parser.exit("Error: --source id given, but no --id found") 49 | if args_.source != 'id' and args_.id is not None: 50 | parser.print_help() 51 | parser.exit("Error: --id given, but --source is not 'id'") 52 | logger.setLevel(getattr(logging, args_.log_level.upper())) 53 | logging.getLogger('msg').setLevel(logging.CRITICAL) 54 | logger.debug("test running args: %s", args_) 55 | return args_ 56 | 57 | if __name__ == "__main__": 58 | try: 59 | args = arguments() 60 | id_list = args.id if args.id is not None else [] 61 | database = setup_database(args, 'twitter') 62 | logger.info("Trying to create mock api from %s", args.source) 63 | twitter = Mock.MockApi(mode=args.source, 64 | id_list=[int(x) for x in id_list]) 65 | 66 | magic_tags, magic_emojis = database.magic_hashtags() 67 | handle_list(network=twitter, 68 | database=database, 69 | magic_tags=magic_tags, 70 | magic_emojis=magic_emojis) 71 | if args.source != 'external': 72 | raise SystemExit(twitter.statistics(args.output)) 73 | except Exception as e: # pylint: disable=W0703 74 | logger.log(51, "TESTS FAIL TO RUN") 75 | logger.exception("%s", e) 76 | -------------------------------------------------------------------------------- /tests/test_network.py: -------------------------------------------------------------------------------- 1 | """Unit tests for AnswerMachine.react.process_commands""" 2 | 3 | import logging 4 | 5 | from Externals.message import Message 6 | from Externals.network import Network 7 | from AnswerMachine.react import process_commands 8 | 9 | log = logging.getLogger('test') 10 | 11 | class MockApi(Network): # pylint: disable=abstract-method 12 | def __init__(self, willfollow, willdefollow): 13 | self.willdefollow = willdefollow 14 | self.willfollow = willfollow 15 | 16 | def is_followed(self, author): 17 | log.critical(author) 18 | return author == "follows" 19 | 20 | def follow(self, _): 21 | assert self.willfollow 22 | 23 | def defollow(self, _): 24 | assert self.willdefollow 25 | 26 | def test_folgenbitte_not_following(): 27 | msg = Message(id=0, text="blabla", author="followsnot", hashtag_texts=['folgenbitte']) 28 | process_commands(msg, MockApi(True, False)) 29 | 30 | def test_folgenbitte_following(): 31 | msg = Message(id=0, text="blabla", author="follows", hashtag_texts=['folgenbitte']) 32 | process_commands(msg, MockApi(False, False)) 33 | 34 | def test_entfolgen_not_following(): 35 | msg = Message(id=0, text="blabla", author="followsnot", hashtag_texts=['entfolgen']) 36 | process_commands(msg, MockApi(False, False)) 37 | 38 | def test_entfolgen_following(): 39 | msg = Message(id=0, text="blabla", author="follows", hashtag_texts=['entfolgen']) 40 | process_commands(msg, MockApi(False, True)) 41 | 42 | def test_both_following(): 43 | msg = Message(id=0, text="blabla", author="follows", 44 | hashtag_texts=['folgenbitte', 'entfolgen']) 45 | process_commands(msg, MockApi(False, True)) 46 | 47 | def test_both_not_following(): 48 | msg = Message(id=0, text="blabla", author="followsnot", 49 | hashtag_texts=['entfolgen', 'folgenbitte']) 50 | process_commands(msg, MockApi(True, True)) 51 | -------------------------------------------------------------------------------- /tests/test_process_commands.py: -------------------------------------------------------------------------------- 1 | """Unit tests for AnswerMachine.react.process_commands""" 2 | 3 | from Externals.message import Message 4 | from AnswerMachine.react import process_commands 5 | 6 | class MockApi: 7 | def __init__(self, willask, willdeask): 8 | self.will_ask = willask 9 | self.will_deask = willdeask 10 | 11 | def handle_followrequest(self, _): 12 | assert self.will_ask 13 | 14 | def handle_defollowrequest(self, _): 15 | assert self.will_deask 16 | 17 | def test_nocommand(): 18 | msg = Message(id=0, text="blabla", author=None, hashtag_texts=[]) 19 | try: 20 | process_commands(msg, None) 21 | except AttributeError: 22 | assert False, "unprocessable Message needs API" 23 | 24 | def test_folgenbitte_not_following(): 25 | msg = Message(id=0, text="blabla", author="followsnot", hashtag_texts=['folgenbitte']) 26 | process_commands(msg, MockApi(True, False)) 27 | 28 | def test_folgenbitte_following(): 29 | msg = Message(id=0, text="blabla", author="follows", hashtag_texts=['folgenbitte']) 30 | process_commands(msg, MockApi(True, False)) 31 | 32 | def test_entfolgen_not_following(): 33 | msg = Message(id=0, text="blabla", author="followsnot", hashtag_texts=['entfolgen']) 34 | process_commands(msg, MockApi(False, True)) 35 | 36 | def test_entfolgen_following(): 37 | msg = Message(id=0, text="blabla", author="follows", hashtag_texts=['entfolgen']) 38 | process_commands(msg, MockApi(False, True)) 39 | 40 | def test_both_following(): 41 | msg = Message(id=0, text="blabla", author="follows", 42 | hashtag_texts=['folgenbitte', 'entfolgen']) 43 | process_commands(msg, MockApi(True, True)) 44 | 45 | def test_both_not_following(): 46 | msg = Message(id=0, text="blabla", author="followsnot", 47 | hashtag_texts=['entfolgen', 'folgenbitte']) 48 | process_commands(msg, MockApi(True, True)) 49 | -------------------------------------------------------------------------------- /tests/test_process_magic.py: -------------------------------------------------------------------------------- 1 | """Unit tests for AnswerMachine.react.process_magic""" 2 | 3 | from AnswerMachine.react import process_magic 4 | 5 | def test_empty(): 6 | assert process_magic([], 13, 'DEFAULT') == [['DEFAULT', [0, 0]], ['__', [13, 13]]] 7 | 8 | def test_movetostart(): 9 | inp = [ 10 | ['ASDF', [3, 6]], 11 | ] 12 | out = [*inp, ['__', [15, 15]]] 13 | assert process_magic(inp, 15, 'D') == out 14 | 15 | def test_keeporder(): 16 | inp = [ 17 | ['ASDF', [0, 6]], 18 | ['DDDD', [9, 6]], 19 | ['ASDF', [0, 5]], 20 | ['AAAA', [0, 8]], 21 | ['ERTF', [4, 2]], 22 | ] 23 | out = [*inp, ['__', [20, 20]]] 24 | assert process_magic(inp, 20, 'D') == out 25 | -------------------------------------------------------------------------------- /tools/add_score_to_commit_msg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pylint_score=$(\ 4 | tools/check | \ 5 | grep has\ been\ rated | \ 6 | grep -o '[0-9]\+\.[0-9][0-9]' | \ 7 | head -n 1 \ 8 | ) 9 | test_result=$(./test --source testcases --output summary --log-level CRITICAL 2>&1) 10 | pytest_result=$(python3 -m pytest tests/ --disable-warnings -q | head -n 1 | tr -s ' ') 11 | 12 | echo "Adding pylint score $pylint_score..." 13 | echo "...and test result $test_result..." 14 | echo "...and pytest result $pytest_result..." 15 | echo "...to commit message:" 16 | 17 | git commit --allow-empty --amend -m "$(git log --format=%B -n1 | grep -v pylint\ score | grep -v test\ score | grep -v pytest\ score) 18 | 19 | pylint score: $pylint_score 20 | test score: $test_result 21 | pytest score: $pytest_result" 22 | 23 | git log --format=%B -n1 24 | -------------------------------------------------------------------------------- /tools/check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pylint \ 4 | --reports=n \ 5 | $(tools/find_all_pys) 6 | -------------------------------------------------------------------------------- /tools/find_all_pys: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git ls-files | grep py$; 4 | grep -RIl '^../usr/bin/python3' | grep -v \.py$ 5 | -------------------------------------------------------------------------------- /tools/manage-blacklist: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """Helper program for management of the bot blacklist""" 4 | 5 | import argparse 6 | from sys import stderr 7 | import parentdir # pylint: disable=W0611 8 | from Externals import setup_database 9 | 10 | parser = argparse.ArgumentParser(description=__doc__) 11 | parser.add_argument('--list', action='store_true', required=False, default=False) 12 | parser.add_argument('--add', action='store', required=False) 13 | # parser.add_argument('--remove', action='store', required=False) 14 | parser.add_argument('--statistics-for', action='store', required=False) 15 | parser.add_argument('--readwrite', help='Change Database', required=False, default=True) 16 | args = parser.parse_args() 17 | 18 | sql = setup_database(args, None) 19 | 20 | if args.list: 21 | sql.cursor.execute(""" 22 | SELECT 23 | source, 24 | Abk 25 | FROM 26 | blacklist 27 | """) 28 | for row in sql.cursor.fetchall(): 29 | print(row['source'], row['Abk']) 30 | raise SystemExit(0) 31 | if not args.statistics_for is None: 32 | print("Statistics for", args.statistics_for) 33 | sql.cursor.execute(""" 34 | SELECT 35 | count(status) as c, 36 | status 37 | FROM 38 | requestlog 39 | WHERE 40 | abbreviation LIKE ? 41 | GROUP BY 42 | status 43 | """, (args.statistics_for, )) 44 | tot_sum = 0 45 | for row in sql.cursor.fetchall(): 46 | tot_sum += row['c'] 47 | print(row['c'], row['status']) 48 | print(tot_sum, "total") 49 | raise SystemExit(0) 50 | if not args.add is None: 51 | sql.cursor.execute(""" 52 | SELECT 53 | 1 54 | FROM 55 | blacklist 56 | WHERE 57 | source || ':' || Abk = ? 58 | """, (args.add, )) 59 | row = sql.cursor.fetchone() 60 | if row is None: 61 | parts = args.add.split(':') 62 | sql.cursor.execute(""" 63 | INSERT INTO 64 | blacklist(source, Abk) 65 | VALUES(?, ?) 66 | """, (parts[0], parts[1],)) 67 | if sql.cursor.rowcount == 0: 68 | print("Keine Daten eingetragen", file=stderr) 69 | raise SystemExit(1) 70 | print("Erfolgreich eingetragen!") 71 | sql.close_sucessfully() 72 | raise SystemExit(0) 73 | print("Schon eingetragen!") 74 | raise SystemExit(0) 75 | -------------------------------------------------------------------------------- /tools/parentdir.py: -------------------------------------------------------------------------------- 1 | """Add parent directory to sys.path for module importing""" 2 | 3 | import sys 4 | from pathlib import Path 5 | sys.path.append(str(Path(__file__).resolve().parents[1])) 6 | -------------------------------------------------------------------------------- /tools/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """Setup of data and documentation""" 4 | 5 | import argparse 6 | import logging 7 | from pathlib import Path 8 | import subprocess 9 | import parentdir # pylint: disable=W0611 10 | import Documentation 11 | from Externals import setup_database 12 | from GitVersion import Git 13 | from Import import find_all_configs 14 | import Persistence 15 | 16 | logger = Persistence.init_logger('setup') 17 | 18 | parser = argparse.ArgumentParser(description=__doc__) 19 | Persistence.set_logging_args(parser, 'INFO') 20 | parser.add_argument('--readwrite', 21 | help='Actually do stuff', 22 | required=False, 23 | default=True) 24 | args = parser.parse_args() 25 | 26 | logger.setLevel(getattr(logging, args.log_level.upper())) 27 | logger.debug("setup running args: %s", args) 28 | 29 | output_dir = Path('output') 30 | dump_dir = output_dir / 'dumps' 31 | config_dir = Path('data') 32 | doc_source = Path('doc') 33 | cardsource = Path('cards') 34 | Path.mkdir(dump_dir, parents=True, exist_ok=True) 35 | 36 | database = setup_database(args, None) 37 | database.purge_data() 38 | 39 | configurations = find_all_configs(config_dir) 40 | if configurations is None: 41 | raise SystemExit(1) 42 | navilinks = Documentation.navilink_list(Git().describe('--dirty')) 43 | dumplinks = Documentation.dumplink_list(configurations) 44 | 45 | Documentation.create_documentation(navilinks, doc_source, output_dir) 46 | Documentation.create_dump_mainpage(navilinks, dumplinks, dump_dir) 47 | dRM = Documentation.MarkdownDoc(Path("data") / 'README.md', navilinks) 48 | dRM.write(output_dir / 'data.html') 49 | sRM = Documentation.MarkdownDoc(Path("sources") / 'README.md', navilinks) 50 | sRM.write(output_dir / 'sources.html') 51 | 52 | licpage = Documentation.Licenses(doc_source / 'copyright.md', navilinks) 53 | 54 | for cid, config in sorted(configurations.items()): 55 | logger.info("Processing file %s", cid) 56 | is_default = True 57 | for a in config.access: 58 | database.insert_source(a, cid, is_default) 59 | is_default = False # only first one. 60 | for mht in config.magic_hashtags: 61 | database.insert_magic_hashtag(cid, mht) 62 | for dl in config.data_list: 63 | if not database.insert_datalist(dl, cid): 64 | raise SystemExit(1) 65 | Documentation.dump_source(config, dumplinks, navilinks, database, dump_dir) 66 | licpage.add_source(config) 67 | 68 | licpage.write(output_dir / 'copyright.html') 69 | Documentation.dump_ignorelist(dumplinks, navilinks, database, dump_dir) 70 | database.close_sucessfully() 71 | 72 | subprocess.run(['chmod', '-R', 'ug=rwX,o=rX', str(output_dir)], check=False) 73 | target_dir = Path('/var/www/ds100/') 74 | avatar_dir = Path('/var/www/avatar/') 75 | if target_dir.exists(): 76 | subprocess.run(['rsync', '-a', str(output_dir) + "/", str(target_dir)], check=False) 77 | for fn in ('bot.css', 'mastodon-icon.png', 'twitter-icon.png', 'script.js'): 78 | subprocess.run(['rsync', '-a', str(doc_source / fn), str(target_dir)], check=False) 79 | subprocess.run(['rsync', '-a', str(cardsource / 'socialcard.png'), str(target_dir)], 80 | check=False) 81 | else: 82 | logger.warning('Not copying documentation') 83 | if avatar_dir.exists(): 84 | subprocess.run(['rsync', '-a', str(doc_source / 'avatar.svg'), str(avatar_dir / 'ds100.svg')], 85 | check=False) 86 | subprocess.run(['rsync', '-a', str(doc_source / 'avatar-ril100.svg'), str(avatar_dir / 87 | 'ril100.svg')], check=False) 88 | else: 89 | logger.warning('Not copying avatar') 90 | 91 | logger.info("Done") 92 | --------------------------------------------------------------------------------