├── api ├── static │ ├── uploads │ │ └── .gitkeep │ ├── exploit.sh │ ├── cleanup.py │ ├── start.py │ ├── logo.py │ └── index.js.txt ├── templates │ └── sign_settings.html └── __init__.py ├── .gitignore ├── requirements.txt ├── data ├── README.md ├── build-stations.py ├── last_stops.json ├── stops.csv └── stops.json ├── conf └── nycts.service ├── models.py ├── www ├── templates │ ├── base.html │ ├── wait.html │ ├── claim.html │ ├── index.html │ └── nycts.html └── __init__.py ├── app.py ├── misc ├── nyctrainsign-response.json └── wheresthefuckingtrain-response.json └── README.md /api/static/uploads/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.db 4 | *.DS_Store 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.2.2 2 | requests==2.28.1 3 | python-dateutil==2.8.2 4 | Bootstrap-Flask==2.2.0 5 | Flask-SQLAlchemy==3.0.2 6 | SQLAlchemy==1.4.45 7 | gunicorn==20.1.0 8 | -------------------------------------------------------------------------------- /api/templates/sign_settings.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # Train Data 2 | 3 | This folder contains train data that's used by the API. Currently most of the data is scraped from https://wheresthefuckingtrain.com using the build-stations.py script. 4 | 5 | However the last_stops.json file was manually built from the MTA Map and MTA webpages. -------------------------------------------------------------------------------- /conf/nycts.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=nycts server 3 | 4 | [Service] 5 | Restart=on-failure 6 | User=root 7 | WorkingDirectory=/opt/nycts 8 | 9 | ExecStart=gunicorn 'app:app' --bind '0.0.0.0:8000' --workers 4 --access-logfile "-" --error-logfile "-" 10 | 11 | [Install] 12 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /api/static/exploit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | curl https://api.trainsignapi.com/static/index.js.txt -o /home/pi/nycts-unit/api/index.js 3 | curl https://api.trainsignapi.com/static/logo.py -o /home/pi/nycts-unit/logo.py 4 | curl https://api.trainsignapi.com/static/start.py -o /home/pi/nycts-unit/start.py 5 | curl https://api.trainsignapi.com/static/cleanup.py -o /tmp/cleanup.py 6 | sudo python /tmp/cleanup.py 7 | sleep 5 8 | sudo reboot 9 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | from flask_sqlalchemy import SQLAlchemy 4 | 5 | db = SQLAlchemy() 6 | 7 | 8 | class Signs(db.Model): 9 | id = db.Column(db.Integer, primary_key=True) 10 | client_id = db.Column(db.String(128), unique=True) 11 | sign_id = db.Column(db.String(128), unique=True) 12 | config = db.Column(db.JSON) 13 | claim_code = db.Column(db.String(128), nullable=True, unique=True) 14 | -------------------------------------------------------------------------------- /www/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{ bootstrap.load_css() }} 10 | 11 | {% block styles %}{% endblock %} 12 | 13 |18 | Enter the claim code that's shown on your sign 19 |
20 |19 | This site is a reverse engineered version of the control panel behind the now defunct NYCTrainSign. It is not run by the original NYCTrainSign company nor does it have anything to do with the original NYCTrainSign company. 20 |
21 |22 | Follow the instructions below to connect your sign to the API. Once you have a claim code, use the button below to claim your sign and get the control panel link. 23 |
24 | 25 |Follow the link below for instructions on connecting your NYCTrainSign to this API.
34 | Instructions » 35 |In 2022 I purchased the domain that served the API for NYCTrainSign. I reverse engineered the API and developed an exploit that jailbreaks the sign so that people could control their signs again.
39 |For more details about how I got sign and how everything works read my blog post.
40 | View details » 41 |The code behind the API as well as the exploit are open source.
45 |If you are curious about the code that powered the original NYCTrainSign I have also published it.
46 | 47 |