├── client_scrapers ├── VERSION ├── scrape songs.bat ├── upload banners.bat ├── scrape stats.bat ├── README.md ├── config.example.php └── upload_banners.php ├── request_list ├── VERSION ├── new.mp3 ├── cancel.mp3 ├── images │ ├── ben.png │ ├── bgs.png │ ├── cafe.png │ ├── ddr.png │ ├── hkc.png │ ├── itg.png │ ├── miku.png │ ├── top.png │ ├── weeb.png │ ├── check.png │ ├── gitgud.png │ ├── hands.png │ ├── holds.png │ ├── jumps.png │ ├── mines.png │ ├── portal.png │ ├── random.png │ ├── rolls.png │ ├── steps.png │ ├── twitch.png │ ├── ddr_arrow.png │ ├── doubles.png │ ├── favicon.ico │ ├── fearmix.png │ ├── gimmick.png │ ├── icon_edit.gif │ ├── singles.png │ ├── theusual.png │ ├── unplayed.png │ ├── icon_heavy.gif │ ├── icon_light.gif │ ├── icon_beginner.gif │ ├── icon_challenge.gif │ ├── icon_standard.gif │ └── packs │ │ ├── unknown.png │ │ └── README.md ├── fonts │ └── fundamentalbrigade.ttf ├── index.php ├── README.md ├── w3-theme-dark-grey.css ├── config.example.php ├── banners.php ├── requestor.php ├── song_admin.php ├── style.css ├── show_requests.php ├── scripts.js ├── broadcaster.php ├── get_updates.php ├── request.php ├── misc_functions.php └── stats.php ├── chatbot ├── Dockerfile ├── package.json ├── bot.js └── package-lock.json ├── docker ├── php │ └── Dockerfile ├── song_sender │ ├── Dockerfile │ └── send_current_song.py └── apache │ ├── Dockerfile │ └── web.apache.conf ├── input_overlay ├── ddrdave │ ├── arrows.png │ ├── stage.png │ └── ddr.ini └── README.md ├── food_widget ├── images │ └── food │ │ ├── 0-dietcoke.png │ │ ├── 100-apple.png │ │ ├── 125-milk.png │ │ ├── 140-coke.png │ │ ├── 180-churro.png │ │ ├── 300-pizza.png │ │ ├── 550-bigmac.png │ │ ├── 800-butter.png │ │ ├── 15-lifesaver.png │ │ ├── 230-icecream.png │ │ ├── 30-broccoli.png │ │ ├── 850-cinnabon.png │ │ ├── 85-chickenwing.png │ │ └── 440-chickensandwich.png └── food.html ├── .gitignore ├── sql ├── mysql_Alter-1.7.sql ├── README.md ├── mysql_Alter-1.72.sql ├── mysql_Alter-1.71.sql └── mysql_schema.sql ├── .env.example ├── chatbot_other ├── README.md ├── StreamElements bot commands.txt └── NightBot_Commands.txt ├── docker-compose.yml └── README.md /client_scrapers/VERSION: -------------------------------------------------------------------------------- 1 | {"version": "1720"} -------------------------------------------------------------------------------- /request_list/VERSION: -------------------------------------------------------------------------------- 1 | {"version": "1720"} -------------------------------------------------------------------------------- /chatbot/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13 2 | 3 | COPY . . 4 | 5 | RUN npm install 6 | 7 | CMD npm start -------------------------------------------------------------------------------- /request_list/new.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/new.mp3 -------------------------------------------------------------------------------- /request_list/cancel.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/cancel.mp3 -------------------------------------------------------------------------------- /docker/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.0-fpm 2 | RUN apt-get update; \ 3 | apt-get upgrade -y; 4 | RUN docker-php-ext-install mysqli -------------------------------------------------------------------------------- /request_list/images/ben.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/ben.png -------------------------------------------------------------------------------- /request_list/images/bgs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/bgs.png -------------------------------------------------------------------------------- /request_list/images/cafe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/cafe.png -------------------------------------------------------------------------------- /request_list/images/ddr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/ddr.png -------------------------------------------------------------------------------- /request_list/images/hkc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/hkc.png -------------------------------------------------------------------------------- /request_list/images/itg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/itg.png -------------------------------------------------------------------------------- /request_list/images/miku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/miku.png -------------------------------------------------------------------------------- /request_list/images/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/top.png -------------------------------------------------------------------------------- /request_list/images/weeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/weeb.png -------------------------------------------------------------------------------- /request_list/images/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/check.png -------------------------------------------------------------------------------- /request_list/images/gitgud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/gitgud.png -------------------------------------------------------------------------------- /request_list/images/hands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/hands.png -------------------------------------------------------------------------------- /request_list/images/holds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/holds.png -------------------------------------------------------------------------------- /request_list/images/jumps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/jumps.png -------------------------------------------------------------------------------- /request_list/images/mines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/mines.png -------------------------------------------------------------------------------- /request_list/images/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/portal.png -------------------------------------------------------------------------------- /request_list/images/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/random.png -------------------------------------------------------------------------------- /request_list/images/rolls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/rolls.png -------------------------------------------------------------------------------- /request_list/images/steps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/steps.png -------------------------------------------------------------------------------- /request_list/images/twitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/twitch.png -------------------------------------------------------------------------------- /docker/song_sender/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY . . 6 | 7 | CMD [ "python","-u","send_current_song.py" ] -------------------------------------------------------------------------------- /input_overlay/ddrdave/arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/input_overlay/ddrdave/arrows.png -------------------------------------------------------------------------------- /input_overlay/ddrdave/stage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/input_overlay/ddrdave/stage.png -------------------------------------------------------------------------------- /request_list/images/ddr_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/ddr_arrow.png -------------------------------------------------------------------------------- /request_list/images/doubles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/doubles.png -------------------------------------------------------------------------------- /request_list/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/favicon.ico -------------------------------------------------------------------------------- /request_list/images/fearmix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/fearmix.png -------------------------------------------------------------------------------- /request_list/images/gimmick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/gimmick.png -------------------------------------------------------------------------------- /request_list/images/icon_edit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/icon_edit.gif -------------------------------------------------------------------------------- /request_list/images/singles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/singles.png -------------------------------------------------------------------------------- /request_list/images/theusual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/theusual.png -------------------------------------------------------------------------------- /request_list/images/unplayed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/unplayed.png -------------------------------------------------------------------------------- /request_list/images/icon_heavy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/icon_heavy.gif -------------------------------------------------------------------------------- /request_list/images/icon_light.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/icon_light.gif -------------------------------------------------------------------------------- /food_widget/images/food/0-dietcoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/0-dietcoke.png -------------------------------------------------------------------------------- /food_widget/images/food/100-apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/100-apple.png -------------------------------------------------------------------------------- /food_widget/images/food/125-milk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/125-milk.png -------------------------------------------------------------------------------- /food_widget/images/food/140-coke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/140-coke.png -------------------------------------------------------------------------------- /food_widget/images/food/180-churro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/180-churro.png -------------------------------------------------------------------------------- /food_widget/images/food/300-pizza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/300-pizza.png -------------------------------------------------------------------------------- /food_widget/images/food/550-bigmac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/550-bigmac.png -------------------------------------------------------------------------------- /food_widget/images/food/800-butter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/800-butter.png -------------------------------------------------------------------------------- /request_list/images/icon_beginner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/icon_beginner.gif -------------------------------------------------------------------------------- /request_list/images/icon_challenge.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/icon_challenge.gif -------------------------------------------------------------------------------- /request_list/images/icon_standard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/icon_standard.gif -------------------------------------------------------------------------------- /request_list/images/packs/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/images/packs/unknown.png -------------------------------------------------------------------------------- /client_scrapers/scrape songs.bat: -------------------------------------------------------------------------------- 1 | ::update path to php installation 2 | @echo off 3 | cd "C:\php" 4 | cls 5 | php.exe "%~dp0scrape_songs_cache.php" 6 | pause 7 | -------------------------------------------------------------------------------- /food_widget/images/food/15-lifesaver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/15-lifesaver.png -------------------------------------------------------------------------------- /food_widget/images/food/230-icecream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/230-icecream.png -------------------------------------------------------------------------------- /food_widget/images/food/30-broccoli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/30-broccoli.png -------------------------------------------------------------------------------- /food_widget/images/food/850-cinnabon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/850-cinnabon.png -------------------------------------------------------------------------------- /food_widget/images/food/85-chickenwing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/85-chickenwing.png -------------------------------------------------------------------------------- /request_list/fonts/fundamentalbrigade.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/request_list/fonts/fundamentalbrigade.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test_files/* 2 | .DS_Store 3 | request_list/images/packs/* 4 | docker/persist/* 5 | client_scrapers/log 6 | client_scrapers/config.php 7 | request_list/config.php -------------------------------------------------------------------------------- /request_list/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /food_widget/images/food/440-chickensandwich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/HEAD/food_widget/images/food/440-chickensandwich.png -------------------------------------------------------------------------------- /client_scrapers/upload banners.bat: -------------------------------------------------------------------------------- 1 | ::update path to php installation and path the php script 2 | @echo off 3 | cd "C:\php" 4 | cls 5 | php.exe "%~dp0upload_banners.php" 6 | pause 7 | -------------------------------------------------------------------------------- /client_scrapers/scrape stats.bat: -------------------------------------------------------------------------------- 1 | ::update path to php installation 2 | @echo off 3 | cd "C:\php" 4 | cls 5 | ::example: php.exe "%~dp0scrape_stats.php" 6 | php.exe "%~dp0scrape_stats.php" 7 | pause -------------------------------------------------------------------------------- /sql/mysql_Alter-1.7.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE sm_requests CHANGE request_type request_type ENUM('normal','random','top','portal','gitgud','theusual','itg','ddr','ben','bgs','gimmick','hkc','weeb','miku') DEFAULT NULL; -------------------------------------------------------------------------------- /request_list/images/packs/README.md: -------------------------------------------------------------------------------- 1 | ## Pack banner images 2 | 3 | Location where banner images are uploaded via the banner uploader script 4 | 5 | `unknown.png` is the fallback banner image for packs with no banner 6 | -------------------------------------------------------------------------------- /sql/README.md: -------------------------------------------------------------------------------- 1 | ## MariaDB SQL Schema 2 | ### For new databases 3 | Import the `mysql_schema.sql` file in your newly created database. 4 | 5 | ### For updating your existing database 6 | Import **all** the `mysql_Alter*.sql` files in order up to the most recent version. -------------------------------------------------------------------------------- /docker/apache/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM httpd:2.4.33-alpine 2 | RUN apk update; \ 3 | apk upgrade; 4 | # Copy apache vhost file to proxy php requests to php-fpm container 5 | COPY web.apache.conf /usr/local/apache2/conf/web.apache.conf 6 | RUN echo "Include /usr/local/apache2/conf/web.apache.conf" \ 7 | >> /usr/local/apache2/conf/httpd.conf -------------------------------------------------------------------------------- /chatbot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatbot", 3 | "version": "1.0.0", 4 | "description": "Chat bot to work with the Stepmania song request system", 5 | "homepage": "https://github.com/DaveLinger/Stepmania-Stream-Tools/README.md", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/DaveLinger/Stepmania-Stream-Tools" 9 | }, 10 | "dependencies": { 11 | "dotenv": "^8.2.0", 12 | "tmi.js": "^1.5.0" 13 | }, 14 | "scripts": { 15 | "start": "node bot.js" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docker/apache/web.apache.conf: -------------------------------------------------------------------------------- 1 | ServerName localhost 2 | 3 | LoadModule deflate_module /usr/local/apache2/modules/mod_deflate.so 4 | LoadModule proxy_module /usr/local/apache2/modules/mod_proxy.so 5 | LoadModule proxy_fcgi_module /usr/local/apache2/modules/mod_proxy_fcgi.so 6 | 7 | 8 | # Proxy .php requests to port 9000 of the php-fpm container 9 | ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php:9000/var/www/html/$1 10 | DocumentRoot /var/www/html/ 11 | 12 | DirectoryIndex index.php 13 | Options Indexes FollowSymLinks 14 | AllowOverride All 15 | Require all granted 16 | 17 | 18 | # Send apache logs to stdout and stderr 19 | CustomLog /proc/self/fd/1 common 20 | ErrorLog /proc/self/fd/2 21 | -------------------------------------------------------------------------------- /request_list/README.md: -------------------------------------------------------------------------------- 1 | # File Descriptions and Usage 2 | 3 | ## Pages/End-points 4 | * banners.php -- end point for the banner uploaded script 5 | * broadcaster.php -- end point to set/control options such as requests toggle and chart limitations 6 | * config.example.php -- example configuration file 7 | * get_updates.php -- end point for getting new requests for the request board 8 | * rand_request.php -- end point for random requests 9 | * request.php -- end point for standard requests 10 | * requestor.php -- end point to whitelist and ban users 11 | * show_requests.php -- song request stream widget 12 | * song_admin.php -- end point for banning songs 13 | * songlist.php -- webpage that shows the public song list 14 | * stats.php -- end point for pulling session/requestor stats 15 | * status.php -- end point for the song and stats scraper 16 | -------------------------------------------------------------------------------- /sql/mysql_Alter-1.72.sql: -------------------------------------------------------------------------------- 1 | -- SQL changes for upgrades from 1.71 to 1.72: 2 | 3 | -- rename columns to match charthash function 4 | ALTER TABLE `sm_songsplayed` 5 | CHANGE COLUMN `steps_hash` `charthash` VARCHAR(50); 6 | 7 | ALTER TABLE `sm_scores` 8 | CHANGE COLUMN `steps_hash` `charthash` VARCHAR(50); 9 | 10 | -- add charthash column to notedata 11 | ALTER TABLE `sm_notedata` 12 | ADD COLUMN `charthash` VARCHAR(50) AFTER `chartstyle`; 13 | 14 | -- add new columns from Stats file 15 | ALTER TABLE `sm_songsplayed` 16 | ADD COLUMN `profile_id` TEXT AFTER `player_guid`; 17 | ALTER TABLE `sm_songsplayed` 18 | ADD COLUMN `profile_type` TEXT AFTER `profile_id`; 19 | 20 | ALTER TABLE `sm_scores` 21 | ADD COLUMN `profile_id` TEXT AFTER `username`; 22 | ALTER TABLE `sm_scores` 23 | ADD COLUMN `profile_type` TEXT AFTER `profile_id`; 24 | 25 | -- force a rebuild of the song cache 26 | UPDATE `sm_songs` SET `checksum` = NULL; 27 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # This file is where you should specify the path to your stepmania cache directory, your SECRET KEY, and your bot configuration. 2 | 3 | # Path to your Stepmania directory that contains the "Cache" folder. If you are running Stepmania in portable mode, this is the 4 | # path to the main directory with the Stepmania executable file, Songs folder, Themes folder, etc. If you are not using portable 5 | # mode, the directory was automatically created somewhere else, like your AppData folder: 6 | 7 | SMDIR=C:/Games/Stepmania 5.1 8 | 9 | # This is the SECRET KEY that is used throughout the software. This is what is used to secure external connections. This prevents 10 | # randos from hitting your API to request songs without your permission, or trigger a song scrape, etc. Change this to something 11 | # else and make note of it. 12 | 13 | SECRET_KEY= 14 | 15 | # These are the variables too configure the song requests bot. BOTUSERNAME is the actual Twitch username of your bot account. 16 | # BOTPASSWORD is the oauth KEY that you generated for your bot account here: https://twitchtokengenerator.com . BOTCHANNEL is 17 | # the actual Twitch channel name you want the bot to operate on. 18 | 19 | BOTUSERNAME=mybot 20 | BOTPASSWORD=oauth:2gz0aify7kmp61xk24gto4bxjyehgg 21 | BOTCHANNEL=mychannel -------------------------------------------------------------------------------- /request_list/w3-theme-dark-grey.css: -------------------------------------------------------------------------------- 1 | .w3-theme-l5 {color:#000 !important; background-color:#f6f6f6 !important} 2 | .w3-theme-l4 {color:#000 !important; background-color:#dfdfdf !important} 3 | .w3-theme-l3 {color:#000 !important; background-color:#c0c0c0 !important} 4 | .w3-theme-l2 {color:#fff !important; background-color:#a0a0a0 !important} 5 | .w3-theme-l1 {color:#fff !important; background-color:#818181 !important} 6 | .w3-theme-d1 {color:#fff !important; background-color:#575757 !important} 7 | .w3-theme-d2 {color:#fff !important; background-color:#4e4e4e !important} 8 | .w3-theme-d3 {color:#fff !important; background-color:#444444 !important} 9 | .w3-theme-d4 {color:#fff !important; background-color:#3a3a3a !important} 10 | .w3-theme-d5 {color:#fff !important; background-color:#303030 !important} 11 | 12 | .w3-theme-light {color:#000 !important; background-color:#f6f6f6 !important} 13 | .w3-theme-dark {color:#fff !important;} 14 | .w3-theme-action {color:#fff !important; background-color:#303030 !important} 15 | 16 | .w3-theme {color:#fff !important; background-color:#616161 !important} 17 | .w3-text-theme {color:#616161 !important} 18 | .w3-border-theme {border-color:#616161 !important} 19 | 20 | .w3-hover-theme:hover {color:#fff !important; background-color:#616161 !important} 21 | .w3-hover-text-theme:hover {color:#616161 !important} 22 | .w3-hover-border-theme:hover {border-color:#616161 !important} -------------------------------------------------------------------------------- /chatbot_other/README.md: -------------------------------------------------------------------------------- 1 | # Chat Bot Setup 2 | *Note: This is legacy information visit [the wiki](https://github.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/wiki/Twitch-Chatbot-Setup) for current bot setup information 3 | 4 | ## StreamElements and NightBot 5 | *StreamElements and NightBot do not support importing of custom bot commands.* 6 | 7 | 1. Replace [URL] with your webiste URL and replace [KEY] with your security key. 8 | 2. Create a new command and paste the response from the text file. 9 | 3. Set user level, cooldowns, and command aliases. 10 | 11 | ## Make your own !random[whatever] commands 12 | * The random commands function by RegEx matching in pack name and chart credit fields. 13 | * Put your URL-encoded expression in the command after `random=`. 14 | * EXAMPLE: Let's say you wanted to make a command that picks a random official DDR/DS song: 15 | 1. Your pack names all have `Dance Dance Revolution` or `Dancing Stage` in them. 16 | 2. URL-encode your names: `Dance+Dance+Revolution`,`Dancing+Stage`. 17 | 3. Add the regex 'OR' character ('|', URL-encoded to '%7C'): `Dance+Dance+Revolution%7CDancing+Stage` 18 | 4. Put this in the URL after `random=`: `random=Dance+Dance+Revolution%7CDancing+Stage` 19 | 5. Final command (using SE variables): `${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=Dance+Dance+Revolution%7CDancing+Stage&song=${queryescape ${1:|0}}}` 20 | -------------------------------------------------------------------------------- /input_overlay/README.md: -------------------------------------------------------------------------------- 1 | ## What's all this? 2 | 3 | Okay, so what you are looking at here is a "ddrdave" folder with 3 files inside. 4 | 5 | This is a "theme" or "profile" for the **[Input Overlay](https://obsproject.com/forum/resources/input-overlay.552/)** plugin for OBS. 6 | 7 | ### Usage 8 | 9 | Unless you are streaming from the PC that is running Stepmania, you need to get the keyboard inputs from your stepmania machine onto your streaming machine, or this won't work. So I use a piece of free software called **[Input Director](https://www.inputdirector.com)** to mirror the keyboard inputs from the Stepmania PC to the streaming PC. There's virtually no latency. Install the software on both PCs, setup your steaming PC as a slave and use the software on your Stepmania PC to "Mirror keyboard input across slaves". 10 | 11 | It doesn't matter where you put this folder. Just stick it somewhere and add an Input Overlay source. In the source properties, select arrows.png as the **Overlay image file** and select ddr.ini as the **Layout config file**. 12 | 13 | Then, for aesthetics, you can add stage.png as an image source BELOW the input overlay source, to make it look like a real DDR stage instead of floating arrows (optional). 14 | 15 | ### Note 16 | 17 | ddr.ini contains configuration for which keypresses correspond to which arrows presses. This is the config file as **I** use it, which is NOT the default stepmania key bindings. So you may need to adjust this to work with your bindings, or adjust your bindings to match this config. 18 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Don't touch anything in this file unless you really know what you're doing. To set up your chat bot or Stepmania directory, edit the .env file instead. 2 | 3 | version: "3.4" 4 | 5 | x-common-variables: &common-variables 6 | MYSQL_DATABASE: SMsonglist 7 | MYSQL_USER: sw_user 8 | MYSQL_PASSWORD: 'Y71o8QcQNn' 9 | SECRET_KEY: ${SECRET_KEY} 10 | 11 | services: 12 | mysql: 13 | image: mariadb 14 | environment: 15 | <<: *common-variables 16 | MYSQL_RANDOM_ROOT_PASSWORD: 'yes' 17 | ports: 18 | - '3306:3306' 19 | networks: 20 | - backend 21 | volumes: 22 | - ./sql/:/docker-entrypoint-initdb.d/ 23 | - ./docker/persist/:/var/lib/mysql 24 | php: 25 | build: './docker/php/' 26 | depends_on: 27 | - mysql 28 | environment: 29 | <<: *common-variables 30 | BANNER_DIR: '/var/www/html/images/packs' 31 | networks: 32 | - backend 33 | volumes: 34 | - ./request_list/:/var/www/html/ 35 | - ./song-scraper/:/var/www/html/scraper/ 36 | - ${SMDIR}:/var/www/smdir 37 | apache: 38 | build: './docker/apache/' 39 | depends_on: 40 | - php 41 | - mysql 42 | networks: 43 | - frontend 44 | - backend 45 | ports: 46 | - "80:80" 47 | volumes: 48 | - ./request_list/:/var/www/html/ 49 | - ./song-scraper/:/var/www/html/scraper/ 50 | - ${SMDIR}:/var/www/smdir 51 | chatbot: 52 | build: './chatbot/' 53 | environment: 54 | <<: *common-variables 55 | BOTUSERNAME: 'your-bot' 56 | BOTPASSWORD: 'oauth:your-key' 57 | BOTCHANNEL: 'your-channel' 58 | URLBASE: 'http://apache' 59 | networks: 60 | - backend 61 | #song_sender: 62 | #build: './docker/song_sender/' 63 | #environment: 64 | #<<: *common-variables 65 | #networks: 66 | #- backend 67 | #volumes: 68 | #- ${SMDIR}:/usr/src/app/sm5 69 | networks: 70 | frontend: 71 | backend: -------------------------------------------------------------------------------- /client_scrapers/README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | These scrapers parse all local StepMania data required to run the request system. A pre-configured PHP cli environment is required. For Windows systems, convenient .bat files are provided. 4 | 5 | ## Scrape Songs Cache 6 | 7 | This script parses the song cache files to add/update songs to the sm_songs and sm_notedata tables. 8 | 9 | ## Banner uploading 10 | 11 | This script finds the first image file in each pack folder, formats the name to match the name of the pack, and uploads it to the server. 12 | 13 | ## Scrape Stats 14 | 15 | The stats scraper is responsible for automatically grabbing all your highscores and recently played songs from your Stats.xml file(s) (usually at the song evaluation screen). The data is used to automatically complete open song requests and provide information for random commands (top, gitgud, etc.). Configure the active local profile(s) or USB profile(s) in your config.php file. 16 | 17 | # Usage (first-run) 18 | * Start StepMania and allow it to build its song cache files 19 | * Run "scrape songs.bat" 20 | * A note on deleting songs or song packs/groups: StepMania does not automatically remove the associated cache files after a song is deleted. Make sure you delete the song's cache file before running the scraper. I recommend disabling 'Fast Load' in StepMania's settings. 21 | * Run "scrape stats.bat" to upload your profile's high scores and songs played records to the DB. Once the script says "Done.", you may close it. 22 | * Run "upload banners.bat" to upload your pack/group banners to the server to later be used as song background images on the request board. 23 | 24 | # Usage 25 | * When you add or remove songs/packs from StepMania: 26 | * Run the "scrape songs.bat" again to update the online songlist 27 | * Run "upload banners.bat" to upload new pack/group banners 28 | * Run "scrape stats.bat" everytime you stream. This script is also responsible for completing/marking-off requests on the request board. 29 | -------------------------------------------------------------------------------- /docker/song_sender/send_current_song.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import urllib 5 | 6 | class Watcher(object): 7 | running = True 8 | refresh_delay_secs = 1 9 | 10 | # Constructor 11 | def __init__(self, watch_file, call_func_on_change=None, *args, **kwargs): 12 | self._cached_stamp = 0 13 | self.filename = watch_file 14 | self.call_func_on_change = call_func_on_change 15 | self.args = args 16 | self.kwargs = kwargs 17 | 18 | # Look for changes 19 | def look(self): 20 | stamp = os.stat(self.filename).st_mtime 21 | if stamp != self._cached_stamp: 22 | self._cached_stamp = stamp 23 | # File has changed, so do something... 24 | print('File changed') 25 | if self.call_func_on_change is not None: 26 | self.call_func_on_change(*self.args, **self.kwargs) 27 | 28 | # Keep watching in a loop 29 | def watch(self): 30 | while self.running: 31 | try: 32 | # Look for changes 33 | time.sleep(self.refresh_delay_secs) 34 | self.look() 35 | except KeyboardInterrupt: 36 | print('\nDone') 37 | break 38 | except FileNotFoundError: 39 | print("File not found!") 40 | pass 41 | except: 42 | print('Unhandled error: %s' % sys.exc_info()[0]) 43 | 44 | # Call this function each time a change happens 45 | def custom_action(text): 46 | print("Calling stats scraper...") 47 | link = "http://apache/scraper/scrape_stats.php?security_key=" + os.getenv('SECRET_KEY') 48 | f = urllib.urlopen(link) 49 | response = f.read() 50 | print(response) 51 | 52 | watch_file = 'sm5/Save/LocalProfiles/00000000/Stats.xml' 53 | 54 | watcher = Watcher(watch_file, custom_action, text='yes, changed') # also call custom action function 55 | watcher.watch() # start the watch going -------------------------------------------------------------------------------- /input_overlay/ddrdave/ddr.ini: -------------------------------------------------------------------------------- 1 | # Input Overlay config v.2 2 | # 3 | # The amount of keys in the overlay 4 | 1_key_count=8 5 | # The type of layout (1 for mouse, 2 for keyboard, 3 for controller) 6 | # Make sure that this value is before all values below! 7 | 1_layout_type=2 8 | # Amount of Key rows 9 | 1_key_rows=3 10 | # Amount of Key Coloumns 11 | 1_key_cols=6 12 | # Global Key size of a key 13 | 1_key_abs_w=37 14 | 1_key_abs_h=37 15 | # Space vertically and horizontally (Negative numbers for offset left/up offset) 16 | 1_key_space_v=0 17 | 1_key_space_h=0 18 | # The amount of buttons per row in the texture file 19 | 1_texture_w=8 20 | # The space between two rows of textures (usually button height * two) if a button takes up 21 | # two times the height or more adjust this value (3px gap will be added by the plugin) 22 | 1_texture_v_space=74 23 | ### 24 | # 25 | # Order of buttons in the texture file 26 | # Special keys need hexadecimal value 27 | # Get them here 28 | # https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx 29 | # 30 | # The Texture needs to contain the buttons in that oder going from left to right 31 | # With the top row containing the texture for unpressed buttons and the bottom 32 | # row for pressed buttons. The plugin calculates a 3px gap between all textures 33 | # and 1 px border around the entire texture 34 | # After the amount of buttons specified in texture_w the plugin will stop searching 35 | # for textures to the right and start in the next row 36 | # 37 | 2_key_order=0x0022,0x0014,0x0024,0x0025,0x0016,0x0017,0x0023,0x0015 38 | # 39 | # Width & Height of the buttons in the order specified above 40 | # These values are multipliers 41 | # Meaning '1' resultes in a width/height that is specified in key_w/key_h 42 | # '2' results in key_w/key_h * 2 43 | 2_key_width=1,1,1,1,1,1,1,1 44 | 2_key_height=1,1,1,1,1,1,1,1 45 | # Coloumn = X, Row = Y postion of the buttons in the order spcified above 46 | # Once again not absolute numbers but rather multipliers 47 | # E.g. key_row=3 means that the y position is 3 times the button height 48 | 2_key_row=0,0,1,1,1,1,2,2 49 | 2_key_col=1,4,0,2,3,5,1,4 -------------------------------------------------------------------------------- /request_list/config.example.php: -------------------------------------------------------------------------------- 1 | 'Dave', 30 | 'mrtwinkles' => 'MRT' 31 | ); 32 | 33 | //User request cooldown interval. This value is a multiplier of active global requests. 34 | //Ex: cooldown minutes = (number of active requests) * ($cooldownMultiplier) 35 | //A value of 0.5 equates to 30 seconds of cooldown per active global request. 36 | $cooldownMultiplier = 0.4; 37 | 38 | //Max requests. Maximum active requests before requests are halted. 39 | //This number should be 10 or less. 40 | $maxRequests = 10; 41 | 42 | //Scoring type. This is the scoring type that will be visible for score-based random commands. 43 | //This does not change how any scores are determined, only which score type is displayed. 44 | //Values must be "itg" or "ddr". ITG scores are in percentage and DDR scores are a number out of 1M. 45 | $scoreType = "itg"; 46 | 47 | //Limit to how many random songs can be requested at once 48 | //DEPRECIATED: will remove in a future version 49 | $max_num = 3; 50 | 51 | //Top percent. Percent of total songs played to use as "top" for use in random queries. 52 | //Default value is "0.1" or top 10% of played songs 53 | $topPercent = 0.1; 54 | 55 | ?> 56 | -------------------------------------------------------------------------------- /request_list/banners.php: -------------------------------------------------------------------------------- 1 | $fileSizeMax){ 40 | die($_FILES['file_contents']['name']."'s image file is too large (max size: ". $fileSizeMax / 1024^2 ."MB)!".PHP_EOL); 41 | } 42 | 43 | $uploadfile = $uploaddir .'/'. $_FILES['file_contents']['name']; 44 | 45 | if(!file_exists($uploadfile)){ 46 | //No banner exists, move the temp uploaded file to the banner directory 47 | if (move_uploaded_file($_FILES['file_contents']['tmp_name'], $uploadfile)) { 48 | echo "Successfully uploaded banner for ".$_FILES['file_contents']['name'].PHP_EOL; 49 | }else{ 50 | echo "Possible file upload attack!".PHP_EOL; 51 | } 52 | }else{ 53 | //a banner has been found, check if the filesize has changed 54 | if(filesize($uploadfile) == $_FILES['file_contents']['size']){ 55 | //filesize is the same, *probably* the same image 56 | echo "File already exists for ".$_FILES['file_contents']['name'].PHP_EOL; 57 | }else{ 58 | //check if any image exists for the pack name 59 | $files = glob($uploaddir."/".pathinfo($_FILES['file_contents']['name'],PATHINFO_FILENAME).".{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF,bmp,BMP}",GLOB_BRACE); 60 | if(count($files) > 0){ 61 | //an image exists for this pack name, but is a different file, remove it 62 | array_map('unlink',$files); 63 | echo "Removed previous banner image for ".$_FILES['file_contents']['name'].PHP_EOL; 64 | } 65 | //update banner image with the newly updated file 66 | if(move_uploaded_file($_FILES['file_contents']['tmp_name'], $uploadfile)) { 67 | echo "Successfully updated banner for ".$_FILES['file_contents']['name'].PHP_EOL; 68 | }else{ 69 | echo "Possible file upload attack!".PHP_EOL; 70 | } 71 | } 72 | } 73 | 74 | die(); 75 | //echo 'Here is some more debugging info:'; 76 | //print_r($_FILES); 77 | ?> -------------------------------------------------------------------------------- /client_scrapers/config.example.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sql/mysql_Alter-1.71.sql: -------------------------------------------------------------------------------- 1 | --SQL changes for upgrades from 1.70 to 1.71: 2 | 3 | -- add new columns for future functionallity 4 | ALTER TABLE sm_songsplayed 5 | ADD COLUMN steps_hash VARCHAR(50) AFTER difficulty, 6 | ADD COLUMN player_guid text AFTER username; 7 | 8 | ALTER TABLE sm_scores 9 | ADD COLUMN steps_hash VARCHAR(50) AFTER difficulty; 10 | -- 11 | -- change request_type from ENUM to TEXT 12 | ALTER TABLE sm_requests 13 | CHANGE request_type request_type text; 14 | -- 15 | -- add song_id indexes to speed up some request queries 16 | ALTER TABLE sm_notedata 17 | ADD INDEX `song_id` (`song_id`) USING BTREE; 18 | 19 | ALTER TABLE sm_scores 20 | ADD INDEX `song_id` (`song_id`) USING BTREE; 21 | 22 | ALTER TABLE sm_songsplayed 23 | ADD INDEX `song_id` (`song_id`) USING BTREE; 24 | -- 25 | -- force a rebuild of the song cache 26 | UPDATE `sm_songs` SET `checksum` = NULL; 27 | 28 | --Due to fixes for proper UTF-8 connection to the db, 29 | --we will need to do some utf-8 convertions of every table, column that would contain malformed utf8 strings 30 | --UPDATE [tabe] SET [column] = CONVERT(cast(CONVERT([column] USING latin1) AS BINARY) USING utf8mb4); 31 | UPDATE `sm_broadcaster` SET `broadcaster` = CONVERT(cast(CONVERT(`broadcaster` USING latin1) AS BINARY) USING utf8mb4); 32 | UPDATE `sm_notedata` SET `song_dir` = CONVERT(cast(CONVERT(`song_dir` USING latin1) AS BINARY) USING utf8mb4); 33 | UPDATE `sm_notedata` SET `chart_name` = CONVERT(cast(CONVERT(`chart_name` USING latin1) AS BINARY) USING utf8mb4); 34 | UPDATE `sm_notedata` SET `description` = CONVERT(cast(CONVERT(`description` USING latin1) AS BINARY) USING utf8mb4); 35 | UPDATE `sm_notedata` SET `chartstyle` = CONVERT(cast(CONVERT(`chartstyle` USING latin1) AS BINARY) USING utf8mb4); 36 | UPDATE `sm_notedata` SET `credit` = CONVERT(cast(CONVERT(`credit` USING latin1) AS BINARY) USING utf8mb4); 37 | UPDATE `sm_notedata` SET `stepfile_name` = CONVERT(cast(CONVERT(`stepfile_name` USING latin1) AS BINARY) USING utf8mb4); 38 | UPDATE `sm_requestors` SET `name` = CONVERT(cast(CONVERT(`name` USING latin1) AS BINARY) USING utf8mb4); 39 | UPDATE `sm_requests` SET `requestor` = CONVERT(cast(CONVERT(`requestor` USING latin1) AS BINARY) USING utf8mb4); 40 | UPDATE `sm_scores` SET `song_dir` = CONVERT(cast(CONVERT(`song_dir` USING latin1) AS BINARY) USING utf8mb4); 41 | UPDATE `sm_scores` SET `title` = CONVERT(cast(CONVERT(`title` USING latin1) AS BINARY) USING utf8mb4); 42 | UPDATE `sm_scores` SET `pack` = CONVERT(cast(CONVERT(`pack` USING latin1) AS BINARY) USING utf8mb4); 43 | UPDATE `sm_scores` SET `username` = CONVERT(cast(CONVERT(`username` USING latin1) AS BINARY) USING utf8mb4); 44 | UPDATE `sm_songs` SET `song_dir` = CONVERT(cast(CONVERT(`song_dir` USING latin1) AS BINARY) USING utf8mb4); 45 | UPDATE `sm_songs` SET `title` = CONVERT(cast(CONVERT(`title` USING latin1) AS BINARY) USING utf8mb4); 46 | UPDATE `sm_songs` SET `subtitle` = CONVERT(cast(CONVERT(`subtitle` USING latin1) AS BINARY) USING utf8mb4); 47 | UPDATE `sm_songs` SET `artist` = CONVERT(cast(CONVERT(`artist` USING latin1) AS BINARY) USING utf8mb4); 48 | UPDATE `sm_songs` SET `pack` = CONVERT(cast(CONVERT(`pack` USING latin1) AS BINARY) USING utf8mb4); 49 | UPDATE `sm_songs` SET `credit` = CONVERT(cast(CONVERT(`credit` USING latin1) AS BINARY) USING utf8mb4); 50 | UPDATE `sm_songsplayed` SET `song_dir` = CONVERT(cast(CONVERT(`song_dir` USING latin1) AS BINARY) USING utf8mb4); 51 | UPDATE `sm_songsplayed` SET `username` = CONVERT(cast(CONVERT(`username` USING latin1) AS BINARY) USING utf8mb4); 52 | -------------------------------------------------------------------------------- /request_list/requestor.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 20 | 21 | function clean_user($user){ 22 | global $conn; 23 | $user = urldecode(trim($user)); 24 | $user = mysqli_real_escape_string($conn,$user); 25 | $user = strtolower($user); 26 | if (strpos($user,'@') === 0){ 27 | $user = substr($user,1); 28 | } 29 | return $user; 30 | } 31 | 32 | function toggle_ban($user){ 33 | 34 | global $conn; 35 | 36 | $sql0 = "SELECT * FROM sm_requestors WHERE name = \"$user\""; 37 | $retval0 = mysqli_query( $conn, $sql0 ); 38 | $numrows = mysqli_num_rows($retval0); 39 | if($numrows == 0){ 40 | echo "User has to request a song before being banned, or be manually added."; 41 | } 42 | 43 | if($numrows == 1){ 44 | $row0 = mysqli_fetch_assoc($retval0); 45 | $id = $row0["id"]; 46 | $banned = $row0["banned"]; 47 | if($banned == "true"){ 48 | $value = "false"; 49 | $response = "Unbanned $user. Don't be a dick."; 50 | }else{ 51 | $value = "true"; 52 | $response = "Banned $user. I'm sorry. it's for the best."; 53 | } 54 | 55 | $sql = "UPDATE sm_requestors SET banned=\"$value\" WHERE id=\"$id\" LIMIT 1"; 56 | mysqli_query( $conn, $sql ); 57 | 58 | echo "$response"; 59 | 60 | } 61 | 62 | } 63 | 64 | function toggle_whitelist($user){ 65 | 66 | global $conn; 67 | 68 | $sql0 = "SELECT * FROM sm_requestors WHERE name = \"$user\""; 69 | $retval0 = mysqli_query( $conn, $sql0 ); 70 | $numrows = mysqli_num_rows($retval0); 71 | if($numrows == 0){ 72 | echo "User has to request a song before being whitelisted, or be manually added."; 73 | } 74 | 75 | if($numrows == 1){ 76 | $row0 = mysqli_fetch_assoc($retval0); 77 | $id = $row0["id"]; 78 | $banned = $row0["whitelisted"]; 79 | if($banned == "true"){ 80 | $value = "false"; 81 | $response = "Unwhitelisted $user. Hope you like cooldowns."; 82 | }else{ 83 | $value = "true"; 84 | $response = "Whitelisted $user. With great power comes great responsibility."; 85 | } 86 | 87 | $sql = "UPDATE sm_requestors SET whitelisted=\"$value\" WHERE id=\"$id\" LIMIT 1"; 88 | mysqli_query( $conn, $sql ); 89 | 90 | echo "$response"; 91 | 92 | } 93 | 94 | } 95 | 96 | //get user who sent the command 97 | if(!isset($_GET["user"])){ 98 | die("Error"); 99 | }elseif(isset($_GET["user"])){ 100 | $user = mysqli_real_escape_string($conn,$_GET["user"]); 101 | } 102 | 103 | //special rules for broadcaster 104 | if(isset($_GET["broadcaster"]) && !empty($_GET["broadcaster"])){ 105 | $broadcaster = mysqli_real_escape_string($conn,$_GET["broadcaster"]); 106 | } 107 | 108 | if(isset($_GET["banuser"]) && !empty($_GET["banuser"])){ 109 | $banUser = clean_user($_GET["banuser"]); 110 | if(strtolower($user) != strtolower($broadcaster) && $banUser == strtolower($broadcaster)){ 111 | die("{$user} -> You don't have that kind of power here!"); 112 | } 113 | toggle_ban($banUser); 114 | die(); 115 | } 116 | 117 | if(isset($_GET["whitelist"]) && !empty($_GET["whitelist"])){ 118 | $whitelistUser = clean_user($_GET["whitelist"]); 119 | if(strtolower($user) != strtolower($broadcaster) && $whitelistUser == strtolower($broadcaster)){ 120 | die("{$user} -> You don't have that kind of power here!"); 121 | } 122 | toggle_whitelist($whitelistUser); 123 | die(); 124 | } 125 | 126 | mysqli_close($conn); 127 | ?> 128 | -------------------------------------------------------------------------------- /request_list/song_admin.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 17 | 18 | function toggle_ban_song($id,$type){ 19 | //1 = ban from all 20 | //2 = ban from random 21 | 22 | global $conn; 23 | 24 | $sql0 = "SELECT * FROM sm_songs WHERE id = \"$id\""; 25 | $retval0 = mysqli_query( $conn, $sql0 ); 26 | 27 | if(mysqli_num_rows($retval0) == 1){ 28 | $row0 = mysqli_fetch_assoc($retval0); 29 | $title = $row0["title"]; 30 | $subtitle = $row0["subtitle"]; 31 | $pack = $row0["pack"]; 32 | $banned = $row0["banned"]; 33 | if($banned != "0"){ 34 | $value = "0"; 35 | $response = "Unbanned $title $subtitle from $pack"; 36 | }elseif($type == "song"){ 37 | $value = "1"; 38 | $response = "Banned $title $subtitle from $pack"; 39 | }elseif($type == "random"){ 40 | $value = "2"; 41 | $response = "Banned (random) $title $subtitle from $pack"; 42 | } 43 | 44 | $sql = "UPDATE sm_songs SET banned=$value WHERE id=$id LIMIT 1"; 45 | $retval = mysqli_query( $conn, $sql ); 46 | 47 | echo "$response"; 48 | 49 | }else{ 50 | echo "Something went wrong."; 51 | } 52 | 53 | } 54 | 55 | //die if the command did not come from the broadcaster 56 | //$user = $_GET["user"]; 57 | //$broadcaster = $_GET["broadcaster"]; 58 | //if(strtolower($user)!==$broadcaster){die("That's gonna be a no from me, dawg.");} 59 | 60 | function parseBanType($argsStr){ 61 | //parse #args to determine if the ban type is for "random" 62 | 63 | $result = array('song'=>'','ban'=>''); 64 | $args = explode("#",$argsStr,2); 65 | $args = array_map("trim",$args); 66 | $result['song'] = $args[0]; 67 | 68 | foreach ($args as $arg){ 69 | switch (strtolower($arg)){ 70 | case "random": 71 | case "randoms": 72 | case "rand": 73 | case "rnd": 74 | $result['ban'] = "random"; 75 | break; 76 | default: 77 | $result['ban'] = "song"; 78 | } 79 | } 80 | 81 | return $result; 82 | } 83 | 84 | if(isset($_GET["bansongid"])){ 85 | $commandArgs = parseBanType($_GET["bansongid"]); 86 | $song = clean($commandArgs["song"]); 87 | $type = $commandArgs["ban"]; 88 | 89 | //lookup by ID 90 | $sql = "SELECT * FROM sm_songs WHERE id = '{$song}' ORDER BY title ASC"; 91 | $retval = mysqli_query( $conn, $sql ); 92 | 93 | if (mysqli_num_rows($retval) == 1) { 94 | while($row = mysqli_fetch_assoc($retval)) { 95 | toggle_ban_song($song,$type); 96 | die(); 97 | } 98 | } else { 99 | echo "Didn't find any songs matching that id!"; 100 | die(); 101 | } 102 | 103 | die(); 104 | } 105 | 106 | if(isset($_GET["bansong"])){ 107 | $commandArgs = parseBanType($_GET["bansong"]); 108 | $song = clean($commandArgs["song"]); 109 | $type = $commandArgs["ban"]; 110 | 111 | //Determine if there's a song with this exact title. If someone requested "Tsugaru", this would match "TSUGARU" but would not match "TSUGARU (Apple Mix)" 112 | $sql = "SELECT * FROM sm_songs WHERE strippedtitle='{$song}' ORDER BY title ASC"; 113 | $retval = mysqli_query( $conn, $sql ); 114 | 115 | if (mysqli_num_rows($retval) == 1) { 116 | while($row = mysqli_fetch_assoc($retval)) { 117 | toggle_ban_song($row["id"],$type); 118 | } 119 | die(); 120 | //end exact match 121 | } 122 | 123 | $sql = "SELECT * FROM sm_songs WHERE strippedtitle LIKE '%{$song}%' ORDER BY title ASC, pack ASC"; 124 | $retval = mysqli_query( $conn, $sql ); 125 | 126 | if (mysqli_num_rows($retval) == 1) { 127 | while($row = mysqli_fetch_assoc($retval)) { 128 | toggle_ban_song($row["id"],$type); 129 | } 130 | die(); 131 | //end one match 132 | } 133 | //no one match 134 | if (mysqli_num_rows($retval) > 0) { 135 | echo "$user => No exact match (!bansongid [id]):"; 136 | $i=1; 137 | while($row = mysqli_fetch_assoc($retval)) { 138 | if($i>4){die();} 139 | echo " [ ".$row["id"]. " -> " .trim($row["title"]." ".$row["subtitle"])." from ".$row["pack"]." ]"; 140 | $i++; 141 | } 142 | } 143 | 144 | die(); 145 | } 146 | 147 | mysqli_close($conn); 148 | 149 | ?> 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stepmania-Stream-Tools-MrTwinkles 2 | # SMRequests Fork 3 | A tool for viewer song requests for live streaming StepMania 5 on Twitch 4 | ## Getting Started 5 | **Check out the new [wiki](https://github.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/wiki/Getting-Started)!** 6 | 7 | --- 8 | ## SMRequests features 9 | * Public songlist webpage that supports searching and display of additional song/chart information. 10 | * Viewers can request songs via Twitch chat. 11 | * Viewers can specify steps-type or difficulties for a request 12 | * Script for song scraping, which iterates through the SM5 Cache directory to index packs, songs, and chart metadata. 13 | * Automatic request completion when using the stats scraper. 14 | * Scraping of the Stats.xml files in the LocalProfile and USB profile directories. 15 | * Support for multiple channels/broadcasters with a single SM5 instance and song database. 16 | * Supports "offline" mode, for SM5 machines with no network access. 17 | * On-stream request board to show current and completed requests, and information about the request, such as steps-type, difficulty, or request type. 18 | * Broadcaster controls for the request board to complete, skip, or ban requests. 19 | * Moderator control for toggling requests on or off via chat commands. 20 | * Script for uploading banner images for each song pack for use with the request board. 21 | * Random requests based on songs played and scores: !top, !random, !gitgud 22 | * Ability to ban songs from being requested or being included in random commands. 23 | * On-stream session stats including recent scores, high score lists, and requestors. 24 | * Ability to whitelist or ban users from making requests. 25 | * Broadcaster can limit requests by steps-type and/or difficulty level. 26 | --- 27 | ## Limitations/Known Bugs 28 | * Only 4-panel "dance" mode is supported. Other modes that are supported by SM5 can be implemented, but they are not as of now. 29 | * Weird things may happen with random commands, if you start with a brand new profile Stats.xml file. 30 | * Stats.xml files from other judgement modes in Simply Love (FA+/Casual) are not supported. 31 | * Currently only one SM5 profile per broadcaster is preferred. The system will function with multiple SM5 profiles (ex. pad profile and a KB profle), but score based commands or calculating top songs may give odd results. 32 | * StepMania 5 does not remove associated song cache files on song deletion. If you delete a song/pack, it is recommended to also delete the corresponding cache file(s) too. Disabling 'Fast Load' is also recommended. 33 | * The song request widget/board requires at least one song to continue to update automatically. 34 | * Sometimes the PHP-CLI scripts will hang. Pressing "enter" will gently encourage the script to get back to work. 35 | 36 | ## Milestones 37 | - [x] Multiple broadcaster support 38 | - [x] Stats.xml scraping 39 | - [x] Support for steps type and difficulties in requests 40 | - [x] Offline mode - support dedicated SM5 machines with no network access 41 | - [x] USB profile support 42 | - [ ] Songlist re-re-write 43 | - [ ] Fix custom chat bot 44 | - [ ] Docker support / Electron app 45 | 46 | --- 47 | --- 48 | 49 | # Stepmania-Stream-Tools (From ddrDave's original fork. Sone information below might be out-of-date.) 50 | Tools and utilities for interacting with Stepmania 5 to provide added features for live streaming. 51 | 52 | ### 0. Stepmania Scene Switching and Song Output 53 | On my stream, I have OBS automatically switch between a "song select/evalution" scene (which shows the whole screen capture), calories burned, face camera, etc) and a "gameplay" scene, which only shows the Player 1 side of the video capture, as well as the input overlay, overhead camera, and current heart rate reading. The way this is accomplished is by having Stepmania output text to a specific text file when it switches to or from one of those screens. 54 | 55 | I also output the currently-being-played song title to a different text file. This allows me to "check off" songs that have been requested, as soon as the song starts. This requires the use of a python script I wrote on the computer running Stepmania to watch for changes to this file, and send them off to a php script on my remote web server to parse. Details in the relevant readme. 56 | 57 | ### 0. Pulsoid Food/Calories Web Widget 58 | I use Pulsoid (free) to display my current heart rate BPM on stream from my Wahoo Tickr heart rate strap. Pulsoid also offers a "calories burned" counter. I copied and modified that page to instead display total calories burned in relation to common food items, similar to DDR A. 59 | 60 | ### 0. DDR Input Indicator 61 | I use an OBS plugin called **[Input Overlay](https://obsproject.com/forum/resources/input-overlay.552/)** to achieve this - I had to make a custom config file and two custom graphics for this, which I'll include here in the repo. The other key factor here is you need to get the keyboard inputs from your stepmania machine onto your streaming machine, or this won't work. So I use a piece of free software called **[Input Director](https://www.inputdirector.com)** to mirror the keyboard inputs from the Stepmania PC to the streaming PC. There's virtually no latency. Install the software on both PCs, setup your steaming PC as a slave and use the software on your Stepmania PC to "Mirror keyboard input across slaves". 62 | -------------------------------------------------------------------------------- /chatbot_other/StreamElements bot commands.txt: -------------------------------------------------------------------------------- 1 | Command: !request 2 | User Level: everyone 3 | Response: ${urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&song=${queryescape ${1:}}} 4 | 5 | Command: !cancel 6 | User Level: everyone 7 | Response: ${urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&cancel=${queryescape ${1|1}}} 8 | 9 | Command: !requestid 10 | User Level: everyone 11 | Response: ${urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&songid=${queryescape ${1:}}} 12 | 13 | Command: !skip 14 | User Level: moderator 15 | Response: ${urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&skip=${queryescape ${1|1}}} 16 | 17 | Command: !random 18 | User Level: everyone 19 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=random&song=${queryescape ${1:|0}}} 20 | 21 | Command: !portal 22 | User Level: everyone 23 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=portal&song=${queryescape ${1:|0}}} 24 | 25 | Command: !top 26 | User Level: everyone 27 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=top&song=${queryescape ${1:|0}}} 28 | 29 | Command: !randomben 30 | User Level: everyone 31 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&type=ben&random=Ben+Speirs%7CBen+Fears&song=${queryescape ${1:|0}}} 32 | 33 | Command: !randomddr 34 | User Level: everyone 35 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&type=ddr&random=Dance+Dance+Revolution%7CDancing+Stage&song=${queryescape ${1:|0}}} 36 | 37 | Command: !randomitg 38 | User Level: everyone 39 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&type=itg&random=ITG%7CIn+The+Groove&song=${queryescape ${1:|0}}} 40 | 41 | Command: !randomfearmix 42 | User Level: everyone 43 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&type=fearmix&random=FEARMIX&song=${queryescape ${1:|0}}} 44 | 45 | Command: !randomcafecursed 46 | User Level: everyone 47 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&type=cafe&random=Cafe+Cursed&song=${queryescape ${1:|0}}} 48 | 49 | Command: !banuser 50 | User Level: moderator 51 | Response: ${urlfetch https://[URL]/requestor.php?security_key=[KEY]&broadcaster=${channel}&game=${game}&banuser=${queryescape ${1:}}} 52 | 53 | Command: !whitelist 54 | User Level: moderator 55 | Response: ${urlfetch https://[URL]/requestor.php?security_key=[KEY]&broadcaster=${channel}&game=${game}&whitelist=${queryescape ${1:}}} 56 | 57 | Command: !bansong 58 | User Level: moderator 59 | Response: ${urlfetch https://[URL]/song_admin.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&game=${game}&bansong=${queryescape ${1:}}} 60 | 61 | Command: !bansongid 62 | User Level: moderator 63 | Response: ${urlfetch https://[URL]/song_admin.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&game=${game}&bansongid=${queryescape ${1:}}} 64 | 65 | Command: !gitgud 66 | User Level: everyone 67 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=gitgud&song=${queryescape ${1:|0}}} 68 | 69 | Command: !randomunplayed 70 | User Level: everyone 71 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=unplayed&song=${queryescape ${1:|0}}} 72 | 73 | Command: !songlist 74 | User Level: everyone 75 | Response: The songlist for requesting songs can be found here: https://[URL]/songlist.php 76 | 77 | Command: !randomroll 78 | User Level: everyone 79 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=roll&num=3&song=${queryescape ${1:|0}}}} 80 | 81 | Command: !theusual 82 | User Level: everyone 83 | Response: ${urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&random=theusual&song=${queryescape ${1:|0}}} 84 | 85 | Command: !requesttoggle 86 | User Level: moderator 87 | Response: ${urlfetch https://[URL]/broadcaster.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&requesttoggle=${queryescape ${1:|0}}} 88 | 89 | Command: !stepstype 90 | User Level: moderator 91 | Response: ${urlfetch https://[URL]/broadcaster.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&stepstype=${queryescape ${1}}} 92 | 93 | Command: !meter 94 | User Level: moderator 95 | Response: ${urlfetch https://[URL]/broadcaster.php?security_key=[KEY]&broadcaster=${channel}&user=${user}&tier=${user.level}&game=${game}&meter=${queryescape ${1}}} 96 | -------------------------------------------------------------------------------- /request_list/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Ubuntu'); 2 | 3 | @font-face { 4 | font-family: 'Fundamental'; 5 | src: url('fonts/fundamentalbrigade.ttf') format('truetype'); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | body { 11 | font-family: 'Fundamental', normal; 12 | background-color: #000; 13 | overflow:auto; 14 | } 15 | 16 | ::-webkit-scrollbar{ 17 | display:none; 18 | } 19 | 20 | h2 { 21 | letter-spacing:0.01em; 22 | font-size:10vw; 23 | margin:0px; 24 | margin-left:2vw; 25 | margin-top:1vw; 26 | text-shadow: 27 | 0.5vw 0.5vw 0.5vw #000, 28 | 3px 3px 0 #000, 29 | -1px -1px 0 #000, 30 | 1px -1px 0 #000, 31 | -1px 1px 0 #000, 32 | 1px 1px 0 #000; 33 | color: white; 34 | position:relative; 35 | line-height: 1.2; 36 | z-index:5; 37 | white-space:nowrap; 38 | overflow:hidden; 39 | text-overflow:ellipsis; 40 | } 41 | 42 | h2a { 43 | letter-spacing:0.01em; 44 | font-size:6vw; 45 | margin:0px; 46 | margin-left:1vw; 47 | margin-right:1vw; 48 | margin-top:1vw; 49 | text-shadow: 50 | 0.25vw 0.25vw 0.25vw #000, 51 | 3px 3px 0 #000, 52 | -1px -1px 0 #000, 53 | 1px -1px 0 #000, 54 | -1px 1px 0 #000, 55 | 1px 1px 0 #000; 56 | color: white; 57 | opacity: 1; 58 | position:relative; 59 | line-height: 1.2; 60 | z-index:5; 61 | white-space:nowrap; 62 | overflow:hidden; 63 | text-overflow:ellipsis; 64 | } 65 | 66 | h3 { 67 | letter-spacing:0.01em; 68 | font-size:5vw; 69 | margin:0px; 70 | margin-left:2vw; 71 | color: white; 72 | position:relative; 73 | line-height: 1.2; 74 | float:left; 75 | /*display:inline-block;*/ 76 | z-index:4; 77 | opacity: 0.85; 78 | text-shadow: 0.25vw 0.25vw 0.5vw rgba(0,0,0,1); 79 | overflow:hidden; 80 | text-overflow:ellipsis; 81 | white-space:nowrap; 82 | } 83 | 84 | h4 { 85 | background-image: url("images/twitch.png"); 86 | background-size: contain; 87 | background-repeat: no-repeat; 88 | letter-spacing:0.01em; 89 | font-size:4vw; 90 | margin:0px; 91 | margin-bottom:2vw; 92 | margin-right:2vw; 93 | padding-left:5vw; 94 | margin-top:1vw; 95 | font-weight:500; 96 | color: white; 97 | z-index: 3; 98 | position:relative; 99 | float:right; 100 | /*display:inline-block;*/ 101 | opacity: 0.85; 102 | text-shadow: 0.25vw 0.25vw 0.5vw rgba(0,0,0,0.9); 103 | -webkit-filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 104 | filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 105 | overflow:hidden; 106 | text-overflow:ellipsis; 107 | white-space:nowrap; 108 | } 109 | 110 | .songrow{ 111 | position:relative; 112 | width:100%; 113 | height:20vw; 114 | white-space:nowrap; 115 | overflow:hidden; 116 | text-overflow:ellipsis; 117 | border-radius: 4vw; 118 | margin-bottom:1vw; 119 | } 120 | 121 | .songrow-bg { 122 | filter: blur(0.4vw) brightness(.75); 123 | -webkit-filter: blur(0.4vw) brightness(.75); 124 | position: absolute; 125 | left: -3px; 126 | top: -50%; 127 | width: 101%; 128 | height: auto; 129 | /* opacity: 0.75; */ 130 | z-index: 1; 131 | } 132 | 133 | .admindiv{ 134 | width:100%; 135 | margin-bottom:1vw; 136 | } 137 | 138 | .adminbuttons{ 139 | font-size:3vw; 140 | font-weight:300; 141 | display:inline-block; 142 | cursor:pointer; 143 | text-align: center; 144 | padding: 1vw; 145 | color:white; 146 | border-radius: 1vw; 147 | } 148 | 149 | .adminbuttons:active { 150 | position:relative; 151 | top:1px; 152 | } 153 | 154 | .completed{ 155 | animation:fadeoff 1s forwards; 156 | } 157 | 158 | .check{ 159 | position:absolute; 160 | top:-5px; 161 | left:5px; 162 | z-index:6; 163 | height:100%; 164 | width:20vw; 165 | opacity:0.85; 166 | margin-left:3vw; 167 | filter:none; 168 | -webkit-filter:none; 169 | } 170 | 171 | .type{ 172 | position:absolute; 173 | right:6vw; 174 | top:1vw; 175 | z-index:2; 176 | height:70%; 177 | width:auto; 178 | opacity:0.70; 179 | float:right; 180 | -webkit-filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 181 | filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 182 | } 183 | 184 | .dance{ 185 | position:absolute; 186 | top:1.7vw; 187 | transform:rotate(45deg); 188 | z-index:6; 189 | height:auto; 190 | -webkit-filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 191 | filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 192 | opacity:0.85; 193 | } 194 | 195 | .single{ 196 | right:1.7vw; 197 | width:4vw; 198 | } 199 | 200 | .double{ 201 | right:0vw; 202 | width:8vw; 203 | } 204 | 205 | .difficulty { 206 | position:absolute; 207 | top: 0; 208 | right: 0; 209 | z-index:6; 210 | border-top: 12vw solid rgb(25, 25, 25); 211 | border-left: 12vw solid transparent; 212 | -webkit-filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 213 | filter: drop-shadow(0.25vw 0.25vw 0.25vw rgba(0,0,0,0.9)); 214 | opacity:0.60; 215 | } 216 | 217 | .beginner { 218 | border-top: 12vw solid rgb(0, 255, 255); 219 | } 220 | 221 | .easy { 222 | border-top: 12vw solid rgb(251, 169, 0); 223 | } 224 | 225 | .medium { 226 | border-top: 12vw solid rgb(250, 0, 160); 227 | } 228 | 229 | .hard { 230 | border-top: 12vw solid rgb(102, 250, 0); 231 | } 232 | 233 | .challenge { 234 | border-top: 12vw solid rgb(112, 104, 250); 235 | } 236 | 237 | .edit { 238 | border-top: 12vw solid rgb(55, 55, 55); 239 | } 240 | 241 | @keyframes wiggle{ 242 | 243 | 0%{ 244 | left:-3vw; 245 | } 246 | 25%{ 247 | left:3vw; 248 | } 249 | 50%{ 250 | left:-1.5vw; 251 | opacity:1; 252 | } 253 | 75%{ 254 | left:1.5vw; 255 | } 256 | 90%{ 257 | left:0.5vw; 258 | } 259 | 100%{ 260 | left:0vw; 261 | opacity:1; 262 | } 263 | } 264 | 265 | @keyframes fadeoff{ 266 | 0%{opacity:1;} 267 | 100%{opacity:0.45;} 268 | } -------------------------------------------------------------------------------- /request_list/show_requests.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 12 | 13 | function format_pack($pack,$requestor){ 14 | $length = 40; 15 | $length = $length - (strlen($requestor) * 0.8); 16 | 17 | $pack = str_ireplace("Dance Dance Revolution","DDR",$pack); 18 | $pack = str_ireplace("DanceDanceRevolution","DDR",$pack); 19 | $pack = str_ireplace("Dancing Stage","DS",$pack); 20 | $pack = str_ireplace("DancingStage","DS",$pack); 21 | $pack = str_ireplace("In The Groove","ITG",$pack); 22 | $pack = str_ireplace("InTheGroove","ITG",$pack); 23 | //$pack = str_ireplace("Ben Speirs","BS",$pack); 24 | //$pack = str_ireplace("JBEAN Exclusives","JBEAN...",$pack); 25 | $pack = preg_replace("/(\(.*\).\(.*\))$/","",$pack,1); 26 | if(strlen($pack) > $length){ 27 | //$pack = trim(substr($pack,0,18))."...".trim(substr($pack,strlen($pack)-7)); 28 | $separator = "..."; 29 | $maxLength = $length - strlen($separator); 30 | $startTrunc = $maxLength / 2; 31 | $truncLength = strlen($pack) - $maxLength; 32 | $pack = substr_replace($pack,$separator,$startTrunc,$truncLength); 33 | } 34 | return $pack; 35 | } 36 | 37 | if(isset($_GET["broadcaster"]) && !empty($_GET["broadcaster"])){ 38 | $broadcaster = $_GET["broadcaster"]; 39 | }else{ 40 | $broadcaster = "%"; 41 | } 42 | 43 | if(!isset($_GET["middle"])){ 44 | 45 | echo ' 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 | '; 58 | 59 | } 60 | //parse url parameter for request board length 61 | if ( isset($_GET['length']) && !empty($_GET['length']) && is_numeric($_GET['length'])) { 62 | //is valid number 63 | $requestWidgetLength = $_GET['length']; 64 | }else{ 65 | //not valid number, use default 66 | $requestWidgetLength = 10; 67 | } 68 | 69 | $sql = "SELECT sm_requests.id AS id, sm_requests.song_id AS song_id, title, subtitle, artist, pack, requestor, request_time, request_type, stepstype, difficulty 70 | FROM sm_requests 71 | JOIN sm_songs ON sm_songs.id = sm_requests.song_id 72 | WHERE (state = 'requested' OR state = 'completed') AND broadcaster LIKE '$broadcaster' 73 | ORDER BY request_time DESC LIMIT $requestWidgetLength"; 74 | $retval = mysqli_query( $conn, $sql ) or die(mysqli_error($conn)); 75 | 76 | while($row = mysqli_fetch_assoc($retval)) { 77 | $request_id = $row["id"]; 78 | $song_id = $row["song_id"]; 79 | $request_time = $row["request_time"]; 80 | $requestor = $row["requestor"]; 81 | $title = $row["title"]; 82 | $subtitle = $row["subtitle"]; 83 | $pack = format_pack($row["pack"],$requestor); 84 | $request_type = strtolower($row["request_type"]); 85 | $stepstype = strtolower($row["stepstype"]); 86 | $difficulty = strtolower($row["difficulty"]); 87 | 88 | $pack_img = strtolower(preg_replace('/\s+/', '_', trim($row["pack"]))); 89 | $pack_img = glob("images/packs/".$pack_img.".{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF,bmp,BMP}", GLOB_BRACE); 90 | if (!$pack_img){ 91 | $pack_img = "images/packs/unknown.png"; 92 | }else{ 93 | $pack_img = "images/packs/".urlencode(basename($pack_img[0])); 94 | } 95 | 96 | if($request_type != "normal"){ 97 | $request_img = glob("images/".$request_type.".{png,PNG,gif,GIF}", GLOB_BRACE); 98 | if (!$request_img){ 99 | $request_img = "images/random.png"; 100 | }else{ 101 | $request_img = "images/".urlencode(basename($request_img[0])); 102 | } 103 | $request_type = ""; 104 | }else{ 105 | $request_type = ""; 106 | } 107 | 108 | if(!empty($stepstype)){ 109 | $stepstype_split = explode("-",$stepstype); 110 | $stepstype = ""; 111 | } 112 | 113 | if(!empty($difficulty)){ 114 | $difficulty = "
"; 115 | }else{ 116 | $difficulty = "
"; 117 | } 118 | 119 | if(empty($stepstype)){$difficulty = "";} 120 | 121 | if($i == 0){ 122 | echo "$request_id\n"; 123 | echo "".urlencode($_GET["security_key"])."\n"; 124 | echo "".urlencode($broadcaster)."\n"; 125 | if(isset($_GET['admin'])){echo "admin\n";} 126 | echo "\n"; 127 | } 128 | 129 | echo "
130 |

$title$subtitle

131 |

$pack

132 |

$requestor

"; 133 | echo $request_type."\n"; 134 | echo $difficulty."\n"; 135 | echo $stepstype."\n"; 136 | echo " 137 | $request_time\n 138 |
\n"; 139 | if(isset($_GET['admin'])){ 140 | echo "
141 | \n 142 | 143 | 144 |
\n"; 145 | } 146 | 147 | $ids[] = $request_id; 148 | $i++; 149 | 150 | } 151 | 152 | if(!is_array($ids) || empty($ids)){ 153 | $oldid = 0; 154 | }else{ 155 | $oldid = min($ids); 156 | } 157 | 158 | echo "{$oldid}\n"; 159 | echo " 160 |
161 | "; 162 | 163 | mysqli_close($conn); 164 | ?> -------------------------------------------------------------------------------- /chatbot_other/NightBot_Commands.txt: -------------------------------------------------------------------------------- 1 | Command: !request 2 | User Level: everyone 3 | Response: $(urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&song=$(querystring)) 4 | 5 | Command: !cancel 6 | User Level: everyone 7 | Response: $(urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&cancel=$(querystring)) 8 | 9 | Command: !requestid 10 | User Level: everyone 11 | Response: $(urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&songid=$(querystring)) 12 | 13 | Command: !skip 14 | User Level: moderator 15 | Response: $(urlfetch https://[URL]/request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&skip=$(querystring)) 16 | 17 | Command: !random 18 | User Level: everyone 19 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=random&song=$(querystring)) 20 | 21 | Command: !portal 22 | User Level: everyone 23 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=portal&song=$(querystring)) 24 | 25 | Command: !top 26 | User Level: everyone 27 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=top&song=$(querystring)) 28 | 29 | Command: !randomben 30 | User Level: everyone 31 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&type=ben&random=Ben+Speirs%7CBen+Fears&song=$(querystring)) 32 | 33 | Command: !randomddr 34 | User Level: everyone 35 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&type=ddr&random=Dance+Dance+Revolution%7CDancing+Stage&song=$(querystring)) 36 | 37 | Command: !randomitg 38 | User Level: everyone 39 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&type=itg&random=ITG%7CIn+The+Groove&song=$(querystring)) 40 | 41 | Command: !randomfearmix 42 | User Level: everyone 43 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&type=fearmix&random=FEARMIX&song=$(querystring)) 44 | 45 | Command: !randomcafecursed 46 | User Level: everyone 47 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&type=cafe&random=Cafe+Cursed&song=$(queryescape)) 48 | 49 | Command: !banuser 50 | User Level: moderator 51 | Response: $(urlfetch https://[URL]/requestor.php?security_key=[KEY]&broadcaster=$(channel)&game=$(twitch $(channel) "{{game}}")&banuser=$(querystring)) 52 | 53 | Command: !whitelist 54 | User Level: moderator 55 | Response: $(urlfetch https://[URL]/requestor.php?security_key=[KEY]&broadcaster=$(channel)&game=$(twitch $(channel) "{{game}}")&whitelist=$(querystring)) 56 | 57 | Command: !bansong 58 | User Level: moderator 59 | Response: $(urlfetch https://[URL]/song_admin.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&game=$(twitch $(channel) "{{game}}")&bansong=$(querystring)) 60 | 61 | Command: !bansongid 62 | User Level: moderator 63 | Response: $(urlfetch https://[URL]/song_admin.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&game=$(twitch $(channel) "{{game}}")&bansongid=$(querystring)) 64 | 65 | Command: !gitgud 66 | User Level: everyone 67 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=gitgud&song=$(querystring)) 68 | 69 | Command: !randomunplayed 70 | User Level: everyone 71 | Response: $(urlfetch https://[URL].smrequests.com/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=unplayed&song=$(querystring)) 72 | 73 | Command: !songlist 74 | User Level: everyone 75 | Response: The songlist for requesting songs can be found here: https://[URL]/songlist.php 76 | 77 | Command: !randomroll 78 | User Level: everyone 79 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=roll&num=3&song=$(querystring)) 80 | 81 | Command: !theusual 82 | User Level: everyone 83 | Response: $(urlfetch https://[URL]/rand_request.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&random=theusual&song=$(querystring)) 84 | 85 | Command: !requesttoggle 86 | User Level: moderator 87 | Response: $(urlfetch https://[URL]/broadcaster.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&requesttoggle=$(querystring)) 88 | 89 | Command: !stepstype 90 | User Level: moderator 91 | Response: $(urlfetch https://[URL]/broadcaster.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&stepstype=$(querystring)) 92 | 93 | Command: !meter 94 | User Level: moderator 95 | Response: $(urlfetch https://[URL]/broadcaster.php?security_key=[KEY]&broadcaster=$(channel)&user=$(twitch $(user) "{{displayName}}")&userid=$(userid)&tier=$(userlevel)&game=$(twitch $(channel) "{{game}}")&meter=$(querystring)) 96 | -------------------------------------------------------------------------------- /request_list/scripts.js: -------------------------------------------------------------------------------- 1 | function new_request(array){ 2 | request_id = array.id; 3 | song_id = array.song_id; 4 | requestor = array.requestor; 5 | request_time = array.request_time; 6 | request_type = array.request_type; 7 | stepstype = array.stepstype; 8 | difficulty = array.difficulty; 9 | title = array.title; 10 | subtitle = array.subtitle; 11 | artist = array.artist; 12 | pack = array.pack; 13 | img = array.img; 14 | 15 | if(request_type){ 16 | request_type = ``; 17 | }else{ 18 | request_type = ""; 19 | } 20 | 21 | if(stepstype){ 22 | stepstype_split = stepstype.split('-'); 23 | stepstype = ``; 24 | }else{ 25 | stepstype = ""; 26 | } 27 | 28 | if(difficulty){ 29 | difficulty = `
`; 30 | }else{ 31 | difficulty = `
`; 32 | } 33 | 34 | if(!stepstype){difficulty = "";} 35 | 36 | console.log("Adding request "+request_id); 37 | 38 | data = ` 48 | `; 49 | if ($("#admin").html()){ 50 | data = data + `
51 | \n 52 | 53 | 54 |
`; 55 | } 56 | 57 | $("#lastid").html(request_id); 58 | $("#middle").prepend(data); 59 | $("#request_"+request_id).slideDown(600); 60 | $("#request_"+request_id).first().css("opacity", "0"); 61 | $("#request_"+request_id).first().css("animation", "wiggle 1.5s forwards"); 62 | $("#new")[0].play(); 63 | 64 | } 65 | 66 | function new_cancel(id){ 67 | request_id = id; 68 | if( $("#request_"+request_id).length ){ 69 | console.log("Canceling request "+request_id); 70 | $("#request_"+request_id).slideUp(600, function() {this.remove(); }); 71 | $("#requestadmin_"+request_id).slideUp(600, function() {this.remove(); }); 72 | $("#cancel")[0].play(); 73 | } 74 | } 75 | 76 | function completion(id){ 77 | request_id = id; 78 | if( $("#request_"+request_id).length ){ 79 | if( $("#request_"+request_id).hasClass("completed") ){ 80 | }else{ 81 | console.log("Completing request "+request_id); 82 | $("#request_"+request_id).removeAttr("style"); 83 | $("#request_"+request_id).addClass("completed"); 84 | $("#requestadmin_"+request_id).slideUp(600, function() {this.remove(); }); 85 | $("#request_"+request_id).append(""); 86 | } 87 | } 88 | } 89 | 90 | function skipped(id){ 91 | request_id = id; 92 | if( $("#request_"+request_id).length ){ 93 | console.log("Skipping request "+request_id); 94 | $("#request_"+request_id).slideUp(600, function() {this.remove(); }); 95 | $("#requestadmin_"+request_id).slideUp(600, function() {this.remove(); }); 96 | $("#cancel")[0].play(); 97 | } 98 | } 99 | 100 | function MarkCompleted(id){ 101 | security_key = $("#security_key").html(); 102 | url = `get_updates.php?security_key=${security_key}&func=MarkCompleted&id=${id}`; 103 | $.ajax({url: url, success: function(result){ 104 | if(result){ 105 | result = JSON.parse(result); 106 | if(result["requestsupdated"] > 0){ 107 | console.log(`Request ${id} marked as Completed`); 108 | refresh_data(); 109 | }}; 110 | } 111 | }); 112 | } 113 | 114 | function MarkSkipped(id){ 115 | security_key = $("#security_key").html(); 116 | url = `get_updates.php?security_key=${security_key}&func=MarkSkipped&id=${id}`; 117 | $.ajax({url: url, success: function(result){ 118 | if(result){ 119 | result = JSON.parse(result); 120 | if(result["requestsupdated"] > 0){ 121 | console.log(`Request ${id} marked as Skipped`); 122 | refresh_data(); 123 | }}; 124 | } 125 | }); 126 | } 127 | 128 | function MarkBanned(id){ 129 | security_key = $("#security_key").html(); 130 | url = `get_updates.php?security_key=${security_key}&func=MarkBanned&id=${id}`; 131 | $.ajax({url: url, success: function(result){ 132 | if(result){ 133 | result = JSON.parse(result); 134 | if(result["requestsupdated"] > 0){ 135 | console.log(`Song from request ${id} marked as Banned`); 136 | refresh_data(); 137 | }}; 138 | } 139 | }); 140 | } 141 | 142 | function refresh_data(){ 143 | lastid = $("#lastid").html(); 144 | oldid = $("#oldid").html(); 145 | security_key = $("#security_key").html(); 146 | broadcaster = $("#broadcaster").html(); 147 | url = `get_updates.php?security_key=${security_key}&broadcaster=${broadcaster}&id=${lastid}&oldid=${oldid}`; 148 | $.ajax({url: url, success: function(result){ 149 | if(result){ 150 | result = JSON.parse(result); 151 | if(result["requests"].length > 0){ 152 | howmany = result["requests"].length; 153 | console.log(howmany+" new request(s)"); 154 | $.each(result["requests"], function( key, value ) { 155 | new_request(value); 156 | }); 157 | }else{ 158 | console.log("No new requests"); 159 | } 160 | if(result["cancels"].length > 0){ 161 | $.each(result["cancels"], function( key, value ) { 162 | new_cancel(value); 163 | }); 164 | } 165 | if(result["completions"].length > 0){ 166 | $.each(result["completions"], function( key, value ) { 167 | completion(value); 168 | }); 169 | } 170 | if(result["skips"].length > 0){ 171 | $.each(result["skips"], function( key, value ) { 172 | skipped(value); 173 | }); 174 | } 175 | 176 | }else{ 177 | console.log("Json error downloading data"); 178 | } 179 | }}); 180 | } 181 | 182 | window.setInterval(function(){ 183 | refresh_data(); 184 | }, 5000); 185 | 186 | $(function() {refresh_data();}); -------------------------------------------------------------------------------- /food_widget/food.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
Total used calories 0kcal
21 |
Reference Food: Diet Coke 0kcal
22 |
23 |
24 | 25 |
26 | 27 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /chatbot/bot.js: -------------------------------------------------------------------------------- 1 | const tmi = require('tmi.js'); 2 | const { exec } = require("child_process"); 3 | const { isNull, isNullOrUndefined } = require('util'); 4 | require('dotenv').config() 5 | 6 | if(isNullOrUndefined(process.env.BOTUSERNAME) || isNullOrUndefined(process.env.BOTPASSWORD) || isNullOrUndefined(process.env.BOTCHANNEL) || isNullOrUndefined(process.env.URLBASE) || isNullOrUndefined(process.env.SECRET_KEY) || (!(process.env.SECRET_KEY))){ 7 | console.log("Invalid environment variables!"); 8 | process.exit(); 9 | } 10 | 11 | // Define configuration options 12 | const opts = { 13 | identity: { 14 | username: process.env.BOTUSERNAME, 15 | password: process.env.BOTPASSWORD 16 | }, 17 | channels: [ 18 | process.env.BOTCHANNEL 19 | ] 20 | }; 21 | 22 | //Define other globals vars 23 | urlbase = process.env.URLBASE; 24 | secretKey = process.env.SECRET_KEY; 25 | 26 | // Create a client with our options 27 | const client = new tmi.client(opts); 28 | 29 | // Register our event handlers (defined below) 30 | client.on('message', onMessageHandler); 31 | client.on('connected', onConnectedHandler); 32 | 33 | // Connect to Twitch: 34 | client.connect(); 35 | 36 | // Called every time a message comes in 37 | function onMessageHandler (target, context, msg, self) { 38 | if (self) { return; } // Ignore messages from the bot 39 | 40 | displayName = context["display-name"]; 41 | emotes = context["emotes"]; 42 | moderator = context["mod"]; 43 | subscriber = context["subscriber"]; 44 | userId = context["user-id"]; 45 | 46 | howManyEmotes = 0; 47 | for (var emoteId in emotes) { 48 | howManyEmotes++; 49 | if(emoteId == "922359"){ 50 | client.say(target, "woof!"); 51 | } 52 | } 53 | if(howManyEmotes < 1 && (msg.indexOf("bandit") !== -1 || msg.indexOf("Bandit") !== -1)){ 54 | client.say(target, "woof!"); 55 | } 56 | 57 | // Remove whitespace from chat message 58 | const commandName = msg.trim(); 59 | 60 | theWholeCommand = commandName.toLowerCase(); 61 | spacePos = theWholeCommand.indexOf(" "); 62 | if(theWholeCommand.includes(" ")){ //If this command has a space... 63 | theCommand = theWholeCommand.substring(0,spacePos+1); //returns the first piece of text from the message, plus the space after it. Like "!request ". 64 | theArgs = theWholeCommand.substring(spacePos+1); //returns the rest of the text after the space. 65 | }else{ 66 | theCommand = theWholeCommand; 67 | theArgs = ""; 68 | } 69 | 70 | if(theCommand.startsWith("!requestid ") || theCommand.startsWith("!rid ") || theCommand.startsWith("!srid ")){ 71 | 72 | if(theArgs == "69" || theArgs == "420" || theArgs == "1234" || theArgs == "12345" || theArgs == "123"){ 73 | client.say(target, "Hurr hurr, good one!"); 74 | }else{ 75 | 76 | encodedURI = encodeURI('/request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&songid='+theArgs); 77 | requestURI = urlbase+encodedURI; 78 | var request = require("request"); 79 | request( 80 | { uri: urlbase+encodedURI }, 81 | function(error, response, body) { 82 | if(!isNullOrUndefined(error)){console.log(error);} 83 | client.say(target, body); 84 | } 85 | ); 86 | 87 | } 88 | 89 | } 90 | 91 | if(theCommand.startsWith("!request ") || theCommand.startsWith("!sr ") || theCommand.startsWith("!songrequest ")){ 92 | 93 | encodedURI = encodeURI('/request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&song='+theArgs); 94 | requestURI = urlbase+encodedURI; 95 | var request = require("request"); 96 | request( 97 | { uri: urlbase+encodedURI }, 98 | function(error, response, body) { 99 | console.log(error); 100 | console.log(body); 101 | client.say(target, body); 102 | } 103 | ); 104 | 105 | } 106 | 107 | if(theCommand.startsWith("!bansong ")){ 108 | if(moderator){ 109 | encodedURI = encodeURI('/song_admin.php?security_key='+secretKey+'&bansong='+theArgs); 110 | requestURI = urlbase+encodedURI; 111 | var request = require("request"); 112 | request( 113 | { uri: urlbase+encodedURI }, 114 | function(error, response, body) { 115 | console.log(error); 116 | console.log(body); 117 | client.say(target, body); 118 | } 119 | ); 120 | }else{ 121 | //Not a mod 122 | } 123 | } 124 | 125 | if(theCommand.startsWith("!banuser ")){ 126 | if(moderator){ 127 | encodedURI = encodeURI('/requestor.php?security_key='+secretKey+'&banuser='+theArgs); 128 | requestURI = urlbase+encodedURI; 129 | var request = require("request"); 130 | request( 131 | { uri: urlbase+encodedURI }, 132 | function(error, response, body) { 133 | console.log(error); 134 | console.log(body); 135 | client.say(target, body); 136 | } 137 | ); 138 | }else{ 139 | //Not a mod 140 | } 141 | } 142 | 143 | if(theCommand == "!cancel "){ 144 | encodedURI = encodeURI('/request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&cancel='+theArgs); 145 | requestURI = urlbase+encodedURI; 146 | var request = require("request"); 147 | request( 148 | { uri: urlbase+encodedURI }, 149 | function(error, response, body) { 150 | console.log(error); 151 | console.log(body); 152 | client.say(target, body); 153 | } 154 | ); 155 | } 156 | 157 | if(theCommand == "!random "){ 158 | if(theArgs == ""){theArgs ="1";} 159 | encodedURI = encodeURI('/rand_request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&random=random&num='+theArgs); 160 | requestURI = urlbase+encodedURI; 161 | var request = require("request"); 162 | request( 163 | { uri: urlbase+encodedURI }, 164 | function(error, response, body) { 165 | console.log(error); 166 | console.log(body); 167 | client.say(target, body); 168 | } 169 | ); 170 | } 171 | 172 | if(theCommand == "!randomben "){ 173 | if(theArgs == ""){theArgs ="1";} 174 | encodedURI = encodeURI('/rand_request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&random=Ben+Speirs&num='+theArgs); 175 | requestURI = urlbase+encodedURI; 176 | var request = require("request"); 177 | request( 178 | { uri: urlbase+encodedURI }, 179 | function(error, response, body) { 180 | console.log(error); 181 | console.log(body); 182 | client.say(target, body); 183 | } 184 | ); 185 | } 186 | 187 | if(theCommand == "!skip "){ 188 | if(theArgs == ""){theArgs ="1";} 189 | if(moderator){ 190 | encodedURI = encodeURI('/request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&skip='+theArgs); 191 | requestURI = urlbase+encodedURI; 192 | var request = require("request"); 193 | request( 194 | { uri: urlbase+encodedURI }, 195 | function(error, response, body) { 196 | console.log(error); 197 | console.log(body); 198 | client.say(target, body); 199 | } 200 | ); 201 | }else{ 202 | //Not a mod 203 | } 204 | } 205 | 206 | if((theCommand == "!songlist" || theCommand == "!songs")){ 207 | client.say(target, `The song list can be found here: https://www.davelinger.com/twitch/songlist.php`); 208 | } 209 | 210 | if(theCommand == "!top "){ 211 | if(theArgs == ""){theArgs ="1";} 212 | encodedURI = encodeURI('/rand_request.php?security_key='+secretKey+'&user='+displayName+'&userid='+userId+'&random=top&num='+theArgs); 213 | requestURI = urlbase+encodedURI; 214 | var request = require("request"); 215 | request( 216 | { uri: urlbase+encodedURI }, 217 | function(error, response, body) { 218 | console.log(error); 219 | console.log(body); 220 | client.say(target, body); 221 | } 222 | ); 223 | } 224 | 225 | if(theCommand.startsWith("!whitelist ")){ 226 | if(moderator){ 227 | encodedURI = encodeURI('/requestor.php?security_key='+secretKey+'&whitelist='+theArgs); 228 | requestURI = urlbase+encodedURI; 229 | var request = require("request"); 230 | request( 231 | { uri: urlbase+encodedURI }, 232 | function(error, response, body) { 233 | console.log(error); 234 | console.log(body); 235 | client.say(target, body); 236 | } 237 | ); 238 | }else{ 239 | //Not a mod 240 | } 241 | } 242 | 243 | } 244 | // Called every time the bot connects to Twitch chat 245 | function onConnectedHandler (addr, port) { 246 | console.log(`* Connected to ${addr}:${port}`); 247 | } 248 | 249 | -------------------------------------------------------------------------------- /request_list/broadcaster.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 16 | 17 | function add_broadcaster($broadcaster){ 18 | 19 | global $conn; 20 | 21 | $sql = "INSERT INTO sm_broadcaster (broadcaster, request_toggle) VALUES (\"$broadcaster\", \"ON\")"; 22 | $retval = mysqli_query( $conn, $sql ); 23 | $the_id = mysqli_insert_id($conn); 24 | 25 | return($the_id); 26 | } 27 | 28 | function getMeterRange(){ 29 | global $conn; 30 | $sql0 = "SELECT MIN(meter) AS min, MAX(meter) AS max FROM sm_notedata"; 31 | $retval0 = mysqli_query( $conn, $sql0 ); 32 | $row0 = mysqli_fetch_assoc($retval0); 33 | $meters = array('min'=>$row0['min'], 'max'=>$row0['max']); 34 | return $meters; 35 | } 36 | 37 | function toggle_requests($broadcaster,$message){ 38 | 39 | global $conn; 40 | 41 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster = \"$broadcaster\""; 42 | $retval0 = mysqli_query( $conn, $sql0 ); 43 | $numrows = mysqli_num_rows($retval0); 44 | if($numrows == 0){ 45 | //settings for this broadcaster are not in the db. Let's set up a blank profile 46 | add_broadcaster($broadcaster); 47 | $retval0 = mysqli_query( $conn, $sql0 ); 48 | $numrows = mysqli_num_rows($retval0); 49 | } 50 | 51 | if($numrows == 1){ 52 | $row0 = mysqli_fetch_assoc($retval0); 53 | $id = $row0["id"]; 54 | $stepstype = $row0["stepstype"]; 55 | $meter = $row0["meter_max"]; 56 | $toggle = $row0["request_toggle"]; 57 | if($toggle == "ON"){ 58 | $requestsDisableResponses = array("Requests are off.","Requests are disabled.","Requests are deactivated."); 59 | $value = "OFF"; 60 | $response = $requestsDisableResponses[array_rand($requestsDisableResponses,1)]; 61 | if(!empty($message)){ 62 | $response = $response ." ". stripslashes($message); 63 | }elseif(empty($message)){ 64 | $message = ""; 65 | } 66 | }else{ 67 | $requestsEnableResponses = array("Requests are enabled. Go nuts!","Requests are enabled. Put 'em in!","Requests are enabled. Request responsibly!"); 68 | $value = "ON"; 69 | $response = $requestsEnableResponses[array_rand($requestsEnableResponses,1)]; 70 | if(!empty($stepstype) || !empty($meter)){ 71 | $response = $response . " Broadcaster limits set to: "; 72 | if(!empty($stepstype)){ 73 | $response = $response . "$stepstype."; 74 | }elseif(!empty($smeter)){ 75 | $response = $response . "a meter limit of $meter."; 76 | }elseif(!empty($stepstype) && !empty($smeter)){ 77 | $response = $response . "$stepstype with a meter limit of $meter."; 78 | } 79 | } 80 | $message = ""; 81 | } 82 | 83 | $sql = "UPDATE sm_broadcaster SET request_toggle=\"$value\", message=\"$message\" WHERE id=\"$id\" LIMIT 1"; 84 | $retval = mysqli_query( $conn, $sql ); 85 | 86 | echo ("$response"); 87 | 88 | } 89 | 90 | } 91 | 92 | function limit_stepstype($broadcaster,$stepstype){ 93 | 94 | global $conn; 95 | 96 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster = \"$broadcaster\""; 97 | $retval0 = mysqli_query( $conn, $sql0 ); 98 | $numrows = mysqli_num_rows($retval0); 99 | if($numrows == 0){ 100 | //settings for this broadcaster are not in the db. Let's set up a blank profile 101 | add_broadcaster($broadcaster); 102 | $retval0 = mysqli_query( $conn, $sql0 ); 103 | $numrows = mysqli_num_rows($retval0); 104 | } 105 | 106 | if($numrows == 1){ 107 | $row0 = mysqli_fetch_assoc($retval0); 108 | $id = $row0["id"]; 109 | $stepstype_db = $row0["stepstype"]; 110 | if(empty($stepstype) && empty($stepstype_db)){ 111 | $response = "No limit present."; 112 | }elseif(empty($stepstype) && !empty($stepstype_db)){ 113 | $response = "Stepstype currently limited to $stepstype_db"; 114 | }elseif(!empty($stepstype)){ 115 | if($stepstype == "-1"|| $stepstype == "disable" || $stepstype == "off" || $stepstype == "remove" || $stepstype == "stop"){ 116 | $response = "Removing steps-type limit."; 117 | $sql = "UPDATE sm_broadcaster SET stepstype=\"\" WHERE id=\"$id\" LIMIT 1"; 118 | $retval = mysqli_query( $conn, $sql ); 119 | }elseif($stepstype == "singles" || $stepstype == "doubles"){ 120 | $response = "Changing steps-type to $stepstype"; 121 | $stepstype = "dance-".substr($stepstype,0,-1); 122 | $sql = "UPDATE sm_broadcaster SET stepstype=\"$stepstype\" WHERE id=\"$id\" LIMIT 1"; 123 | $retval = mysqli_query( $conn, $sql ); 124 | }else{ 125 | $response = "Invalid steps-type. Useage: \"singles\", \"doubles\", or \"off\"."; 126 | } 127 | } 128 | echo "$response"; 129 | } 130 | 131 | } 132 | 133 | function change_meter($broadcaster,$meter){ 134 | 135 | global $conn; 136 | 137 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster = \"$broadcaster\""; 138 | $retval0 = mysqli_query( $conn, $sql0 ); 139 | $numrows = mysqli_num_rows($retval0); 140 | if($numrows == 0){ 141 | //settings for this broadcaster are not in the db. Let's set up a blank profile 142 | add_broadcaster($broadcaster); 143 | $retval0 = mysqli_query( $conn, $sql0 ); 144 | $numrows = mysqli_num_rows($retval0); 145 | } 146 | 147 | if($numrows == 1){ 148 | $row0 = mysqli_fetch_assoc($retval0); 149 | $id = $row0["id"]; 150 | $meter_db = $row0["meter_max"]; 151 | if(empty($meter) && empty($meter_db)){ 152 | $response = "No difficulty limit present."; 153 | }elseif(empty($meter) && !empty($meter_db)){ 154 | $response = "Difficulty meter currently limited to $meter_db"; 155 | }elseif(!empty($meter)){ 156 | if($meter == "-1" || $meter == "disable" || $meter == "off" || $meter == "remove" || $meter == "stop"){ 157 | $response = "Removing difficulty meter limit."; 158 | $sql = "UPDATE sm_broadcaster SET meter_max=\"\" WHERE id=\"$id\" LIMIT 1"; 159 | $retval = mysqli_query( $conn, $sql ); 160 | }elseif(is_numeric($meter)){ 161 | $response = "Changing max difficulty meter to $meter"; 162 | $sql = "UPDATE sm_broadcaster SET meter_max=\"$meter\" WHERE id=\"$id\" LIMIT 1"; 163 | $retval = mysqli_query( $conn, $sql ); 164 | } 165 | } 166 | echo "$response"; 167 | } 168 | 169 | } 170 | 171 | if(isset($_GET["requesttoggle"])){ 172 | if(strlen($_GET["requesttoggle"]) >= 80){ 173 | die("Custom message must be 80 characters or less."); 174 | } 175 | $message = trim(mysqli_real_escape_string($conn, $_GET["requesttoggle"])); 176 | toggle_requests(strtolower($_GET["broadcaster"]),$message); 177 | } 178 | 179 | if(isset($_GET["stepstype"])){ 180 | $stepstype = trim(mysqli_real_escape_string($conn,$_GET["stepstype"])); 181 | limit_stepstype($_GET["broadcaster"],$stepstype); 182 | } 183 | 184 | if(isset($_GET["meter"])){ 185 | $meter = trim(mysqli_real_escape_string($conn,$_GET["meter"])); 186 | //if($meter < getMeterRange()['min'] || $meter > getMeterRange()['max']){ 187 | // die("Invalid meter given."); 188 | //} 189 | change_meter($_GET["broadcaster"],$meter); 190 | } 191 | 192 | mysqli_close($conn); 193 | die(); 194 | ?> -------------------------------------------------------------------------------- /request_list/get_updates.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 11 | 12 | function format_pack($pack,$requestor){ 13 | $length = 40; 14 | $length = $length - (strlen($requestor) * 0.8); 15 | 16 | $pack = str_ireplace("Dance Dance Revolution","DDR",$pack); 17 | $pack = str_ireplace("DanceDanceRevolution","DDR",$pack); 18 | $pack = str_ireplace("Dancing Stage","DS",$pack); 19 | $pack = str_ireplace("DancingStage","DS",$pack); 20 | $pack = str_ireplace("In The Groove","ITG",$pack); 21 | $pack = str_ireplace("InTheGroove","ITG",$pack); 22 | //$pack = str_ireplace("Ben Speirs","BS",$pack); 23 | //$pack = str_ireplace("JBEAN Exclusives","JBEAN...",$pack); 24 | $pack = preg_replace("/(\(.*\).\(.*\))$/","",$pack,1); 25 | if(strlen($pack) > $length){ 26 | //$pack = trim(substr($pack,0,18))."...".trim(substr($pack,strlen($pack)-7)); 27 | $separator = "..."; 28 | $maxLength = $length - strlen($separator); 29 | $startTrunc = $maxLength / 2; 30 | $truncLength = strlen($pack) - $maxLength; 31 | $pack = substr_replace($pack,$separator,$startTrunc,$truncLength); 32 | } 33 | return $pack; 34 | } 35 | 36 | //Get new requests, cancels, and completions 37 | 38 | function get_cancels_since($id,$oldid,$broadcaster){ 39 | 40 | global $conn; 41 | $sql = "SELECT * FROM sm_requests WHERE id >= $oldid AND state =\"canceled\" AND broadcaster LIKE \"{$broadcaster}\" ORDER BY id ASC"; 42 | $retval = mysqli_query( $conn, $sql ) or die(mysqli_error($conn)); 43 | $cancels = Array(); 44 | while($row = mysqli_fetch_assoc($retval)) { 45 | $request_id = $row["id"]; 46 | array_push($cancels, $request_id); 47 | } 48 | 49 | return $cancels; 50 | 51 | } 52 | 53 | function get_requests_since($id,$oldid,$broadcaster){ 54 | 55 | global $conn; 56 | $requests = array(); 57 | $sql = "SELECT sm_requests.id AS id, sm_requests.song_id AS song_id, title, subtitle, artist, pack, requestor, request_time, request_type, stepstype, difficulty 58 | FROM sm_requests 59 | JOIN sm_songs ON sm_songs.id = sm_requests.song_id 60 | WHERE sm_requests.id > $id AND state = 'requested' AND broadcaster LIKE '$broadcaster' 61 | ORDER by id ASC"; 62 | $retval = mysqli_query( $conn, $sql ) or die(mysqli_error($conn)); 63 | 64 | while($request = mysqli_fetch_assoc($retval)) { 65 | 66 | //format pack name and find pack banner 67 | $pack_img = strtolower(preg_replace('/\s+/', '_', trim($request["pack"]))); 68 | $pack_img = glob("images/packs/".$pack_img.".{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF,bmp,BMP}", GLOB_BRACE); 69 | if (!$pack_img){ 70 | $request["img"] = "images/packs/unknown.png"; 71 | }else{ 72 | $request["img"] = "images/packs/".urlencode(basename($pack_img[0])); 73 | } 74 | $request["pack"] = format_pack($request["pack"],$request["requestor"]); 75 | 76 | //format request type and find image 77 | $request["request_type"] = strtolower($request["request_type"]); 78 | if($request["request_type"] != "normal"){ 79 | $request_img = glob("images/".$request["request_type"].".{png,PNG,gif,GIF}", GLOB_BRACE); 80 | if (!$request_img){ 81 | $request["request_type"] = "images/random.png"; 82 | }else{ 83 | $request["request_type"] = "images/".urlencode(basename($request_img[0])); 84 | } 85 | }else{ 86 | $request["request_type"] = ""; 87 | } 88 | 89 | //format stepstype & difficulty 90 | $request["stepstype"] = strtolower($request["stepstype"]); 91 | $request["difficulty"] = strtolower($request["difficulty"]); 92 | 93 | array_push($requests, $request); 94 | 95 | } 96 | 97 | return $requests; 98 | 99 | } 100 | 101 | function get_completions_since($id,$oldid,$broadcaster){ 102 | 103 | global $conn; 104 | //$id=$id-50; 105 | $sql = "SELECT id FROM sm_requests WHERE id >= $oldid AND state = \"completed\" AND broadcaster LIKE \"{$broadcaster}\""; 106 | $retval = mysqli_query( $conn, $sql ) or die(mysqli_error($conn)); 107 | $completions = Array(); 108 | while($row = mysqli_fetch_assoc($retval)) { 109 | $request_id = $row["id"]; 110 | array_push($completions, $request_id); 111 | } 112 | 113 | return $completions; 114 | 115 | } 116 | 117 | function get_skips_since($id,$oldid,$broadcaster){ 118 | 119 | global $conn; 120 | $sql = "SELECT * FROM sm_requests WHERE id >= $oldid AND state =\"skipped\" AND broadcaster LIKE \"{$broadcaster}\" ORDER BY id ASC"; 121 | $retval = mysqli_query( $conn, $sql ) or die(mysqli_error($conn)); 122 | $skips = Array(); 123 | while($row = mysqli_fetch_assoc($retval)) { 124 | $request_id = $row["id"]; 125 | array_push($skips, $request_id); 126 | } 127 | 128 | return $skips; 129 | 130 | } 131 | 132 | //mark completed or skipped for "offline mode" 133 | 134 | function MarkCompleted($requestid){ 135 | 136 | global $conn; 137 | $requestupdated = 0; 138 | 139 | $sql0 = "SELECT * FROM sm_requests WHERE id = \"$requestid\" AND state <> \"completed\""; 140 | $retval0 = mysqli_query( $conn, $sql0 ); 141 | $numrows = mysqli_num_rows($retval0); 142 | if($numrows == 0){ 143 | die(); 144 | //die("Marked Complete request could not be found."); 145 | } 146 | 147 | if($numrows == 1){ 148 | $row0 = mysqli_fetch_assoc($retval0); 149 | 150 | $sql = "UPDATE sm_requests SET state=\"completed\" WHERE id=\"$requestid\" LIMIT 1"; 151 | $retval = mysqli_query( $conn, $sql ); 152 | 153 | //echo "Request ".$requestid." updated to Completed"; 154 | $requestupdated = 1; 155 | } else { 156 | //echo "Too many requests found."; 157 | } 158 | return $requestupdated; 159 | } 160 | 161 | function MarkSkipped($requestid){ 162 | 163 | global $conn; 164 | $requestupdated = 0; 165 | 166 | $sql0 = "SELECT * FROM sm_requests WHERE id = \"$requestid\" AND state <> \"completed\""; 167 | $retval0 = mysqli_query( $conn, $sql0 ); 168 | $numrows = mysqli_num_rows($retval0); 169 | if($numrows == 0){ 170 | die(); 171 | //die("Mark Skipped request could not be found."); 172 | } 173 | 174 | if($numrows == 1){ 175 | $row0 = mysqli_fetch_assoc($retval0); 176 | 177 | $sql = "UPDATE sm_requests SET state=\"skipped\" WHERE id=\"$requestid\" LIMIT 1"; 178 | $retval = mysqli_query( $conn, $sql ); 179 | 180 | //echo "Request ".$requestid." updated to skipped"; 181 | $requestupdated = 1; 182 | } else { 183 | //echo "Too many requests found."; 184 | } 185 | return $requestupdated; 186 | } 187 | 188 | function MarkBanned($requestid){ 189 | 190 | global $conn; 191 | $requestupdated = 0; 192 | 193 | $sql0 = "SELECT * FROM sm_requests WHERE id = \"$requestid\" AND state <> \"completed\""; 194 | $retval0 = mysqli_query( $conn, $sql0 ); 195 | $numrows = mysqli_num_rows($retval0); 196 | if($numrows == 0){ 197 | die(); 198 | //die("Mark Banned request could not be found."); 199 | } 200 | 201 | if($numrows == 1){ 202 | $row0 = mysqli_fetch_assoc($retval0); 203 | $song_id = $row0['song_id']; 204 | 205 | $sql = "UPDATE sm_songs SET banned = 1 WHERE id=\"$song_id\" LIMIT 1"; 206 | $retval = mysqli_query( $conn, $sql ); 207 | 208 | $sql = "UPDATE sm_requests SET state=\"skipped\" WHERE id=\"$requestid\" LIMIT 1"; 209 | $retval = mysqli_query( $conn, $sql ); 210 | 211 | //echo "Song from request ".$requestid." updated to banned"; 212 | $requestupdated = 1; 213 | } else { 214 | //echo "Too many requests found."; 215 | } 216 | return $requestupdated; 217 | } 218 | 219 | if(!isset($_GET["id"])){die("You must specify an id");} 220 | 221 | $id = $_GET["id"]; 222 | 223 | $output = array(); 224 | 225 | if(isset($_GET["func"])){ 226 | switch($_GET["func"]){ 227 | case "MarkCompleted": 228 | $requestupdated = MarkCompleted($id); 229 | break; 230 | case "MarkSkipped": 231 | $requestupdated = MarkSkipped($id); 232 | break; 233 | case "MarkBanned": 234 | $requestupdated = MarkBanned($id); 235 | break; 236 | default: 237 | die(); 238 | //die("Your function is in another castle."); 239 | } 240 | 241 | $output["requestsupdated"] = $requestupdated; 242 | 243 | }elseif(!isset($_GET["func"])){ 244 | if(!empty($_GET["oldid"])){ 245 | $oldid = $_GET["oldid"]; 246 | }else{ 247 | $oldid = 0; 248 | } 249 | 250 | if(isset($_GET["broadcaster"]) && !empty($_GET["broadcaster"])){ 251 | $broadcaster = $_GET["broadcaster"]; 252 | }else{ 253 | $broadcaster = "%"; 254 | } 255 | 256 | $output["cancels"] = get_cancels_since($id,$oldid,$broadcaster); 257 | 258 | $output["requests"] = get_requests_since($id,$oldid,$broadcaster); 259 | 260 | $output["completions"] = get_completions_since($id,$oldid,$broadcaster); 261 | 262 | $output["skips"] = get_skips_since($id,$oldid,$broadcaster); 263 | 264 | } 265 | 266 | $output = json_encode($output); 267 | 268 | echo "$output"; 269 | 270 | mysqli_close($conn); 271 | 272 | ?> -------------------------------------------------------------------------------- /sql/mysql_schema.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.9.5 3 | -- https://www.phpmyadmin.net/ 4 | -- 5 | -- Generation Time: Feb 22, 2021 at 09:08 PM 6 | -- Server version: 10.3.27-MariaDB 7 | -- PHP Version: 7.3.6 8 | 9 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 10 | SET AUTOCOMMIT = 0; 11 | START TRANSACTION; 12 | SET time_zone = "+00:00"; 13 | 14 | 15 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 16 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 17 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 18 | /*!40101 SET NAMES utf8mb4 */; 19 | 20 | -- -------------------------------------------------------- 21 | 22 | -- 23 | -- Table structure for table `sm_broadcaster` 24 | -- 25 | 26 | CREATE TABLE `sm_broadcaster` ( 27 | `id` int(11) NOT NULL, 28 | `broadcaster` text DEFAULT NULL, 29 | `stepstype` text DEFAULT NULL, 30 | `meter_min` varchar(11) DEFAULT NULL, 31 | `meter_max` varchar(11) DEFAULT NULL, 32 | `request_toggle` enum('ON','OFF') DEFAULT NULL, 33 | `message` mediumtext DEFAULT NULL 34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 35 | 36 | -- -------------------------------------------------------- 37 | 38 | -- 39 | -- Table structure for table `sm_grade_tiers` 40 | -- 41 | 42 | CREATE TABLE `sm_grade_tiers` ( 43 | `percentdp` double(7,2) DEFAULT NULL, 44 | `ddr_tier` text DEFAULT NULL, 45 | `ddr_grade` varchar(50) DEFAULT NULL, 46 | `itg_tier` text DEFAULT NULL, 47 | `itg_grade` varchar(50) DEFAULT NULL 48 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 49 | 50 | -- 51 | -- Dumping data for table `sm_grade_tiers` 52 | -- 53 | 54 | REPLACE INTO `sm_grade_tiers` (`percentdp`, `ddr_tier`, `ddr_grade`, `itg_tier`, `itg_grade`) VALUES 55 | (1.00, 'Tier01', 'AAA+', 'Tier01', '★★★★'), 56 | (0.99, 'Tier02', 'AAA', 'Tier02', '★★★'), 57 | (0.98, NULL, 'AA+', 'Tier03', '★★'), 58 | (0.96, NULL, 'AA+', 'Tier04', '★'), 59 | (0.95, 'Tier03', 'AA+', NULL, 'S+'), 60 | (0.94, NULL, 'AA', 'Tier05', 'S+'), 61 | (0.92, NULL, 'AA', 'Tier06', 'S'), 62 | (0.90, 'Tier04', 'AA', NULL, 'S-'), 63 | (0.89, 'Tier05', 'AA-', 'Tier07', 'S-'), 64 | (0.86, NULL, 'A+', 'Tier08', 'A+'), 65 | (0.85, 'Tier06', 'A+', NULL, 'A'), 66 | (0.83, NULL, 'A', 'Tier09', 'A'), 67 | (0.80, 'Tier07', 'A', 'Tier10', 'A-'), 68 | (0.79, 'Tier08', 'A-', NULL, 'B+'), 69 | (0.76, NULL, 'B+', 'Tier11', 'B+'), 70 | (0.75, 'Tier09', 'B+', NULL, 'B'), 71 | (0.72, NULL, 'B', 'Tier12', 'B'), 72 | (0.70, 'Tier10', 'B', NULL, 'B-'), 73 | (0.69, 'Tier11', 'B-', 'Tier13', 'B-'), 74 | (0.68, NULL, 'C+', NULL, 'C+'), 75 | (0.65, 'Tier12', 'C+', NULL, 'C+'), 76 | (0.64, NULL, 'C', 'Tier14', 'C+'), 77 | (0.60, 'Tier13', 'C', 'Tier15', 'C-'), 78 | (0.59, 'Tier14', 'C-', NULL, 'C-'), 79 | (0.55, 'Tier15', 'D+', 'Tier16', 'C-'), 80 | (0.00, 'Tier17', 'D', NULL, 'D'), 81 | (-99999.00, NULL, 'FAILED', 'Tier17', 'D'), 82 | (0.50, 'Tier16', 'D', NULL, 'D'); 83 | 84 | -- -------------------------------------------------------- 85 | 86 | -- 87 | -- Table structure for table `sm_notedata` 88 | -- 89 | 90 | CREATE TABLE `sm_notedata` ( 91 | `id` mediumint(9) NOT NULL, 92 | `song_id` mediumint(9) DEFAULT NULL, 93 | `song_dir` mediumtext DEFAULT NULL, 94 | `chart_name` text DEFAULT NULL, 95 | `stepstype` text DEFAULT NULL, 96 | `description` text DEFAULT NULL, 97 | `chartstyle` text DEFAULT NULL, 98 | `charthash` varchar(50) DEFAULT NULL, 99 | `difficulty` text DEFAULT NULL, 100 | `meter` int(11) DEFAULT NULL, 101 | `radar_values` text DEFAULT NULL, 102 | `credit` text DEFAULT NULL, 103 | `display_bpm` varchar(50) DEFAULT NULL, 104 | `stepfile_name` mediumtext DEFAULT NULL, 105 | `datetime` datetime DEFAULT NULL 106 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; 107 | 108 | -- -------------------------------------------------------- 109 | 110 | -- 111 | -- Table structure for table `sm_requestors` 112 | -- 113 | 114 | CREATE TABLE `sm_requestors` ( 115 | `id` int(11) NOT NULL, 116 | `twitchid` int(11) DEFAULT NULL, 117 | `name` varchar(255) DEFAULT NULL, 118 | `whitelisted` enum('true','false') DEFAULT 'false', 119 | `banned` enum('true','false') DEFAULT 'false', 120 | `dateadded` datetime DEFAULT NULL 121 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 122 | 123 | -- -------------------------------------------------------- 124 | 125 | -- 126 | -- Table structure for table `sm_requests` 127 | -- 128 | 129 | CREATE TABLE `sm_requests` ( 130 | `id` int(11) NOT NULL, 131 | `song_id` int(11) DEFAULT NULL, 132 | `request_time` datetime DEFAULT NULL, 133 | `requestor` varchar(255) DEFAULT NULL, 134 | `twitch_tier` varchar(255) DEFAULT NULL, 135 | `broadcaster` tinytext DEFAULT NULL, 136 | `state` enum('requested','canceled','completed','skipped') DEFAULT 'requested', 137 | `request_type` text DEFAULT NULL, 138 | `stepstype` tinytext DEFAULT NULL, 139 | `difficulty` tinytext DEFAULT NULL, 140 | `timestamp` datetime DEFAULT NULL ON UPDATE current_timestamp() 141 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 142 | 143 | -- -------------------------------------------------------- 144 | 145 | -- 146 | -- Table structure for table `sm_scores` 147 | -- 148 | 149 | CREATE TABLE `sm_scores` ( 150 | `id` int(11) NOT NULL, 151 | `song_dir` text DEFAULT NULL, 152 | `song_id` int(11) DEFAULT NULL, 153 | `title` text DEFAULT NULL, 154 | `pack` text DEFAULT NULL, 155 | `stepstype` mediumtext DEFAULT NULL, 156 | `difficulty` text DEFAULT NULL, 157 | `charthash` VARCHAR(50) DEFAULT NULL, 158 | `username` tinytext DEFAULT NULL, 159 | `profile_id` text DEFAULT NULL, 160 | `profile_type` text DEFAULT NULL, 161 | `grade` tinytext DEFAULT NULL, 162 | `score` bigint(20) DEFAULT NULL, 163 | `percentdp` decimal(10,6) DEFAULT NULL, 164 | `modifiers` text DEFAULT NULL, 165 | `datetime` datetime DEFAULT NULL, 166 | `survive_seconds` decimal(10,6) DEFAULT NULL, 167 | `life_remaining_seconds` decimal(10,6) DEFAULT NULL, 168 | `disqualified` tinyint(4) DEFAULT NULL, 169 | `max_combo` smallint(6) DEFAULT NULL, 170 | `stage_award` text DEFAULT NULL, 171 | `peak_combo_award` text DEFAULT NULL, 172 | `player_guid` text DEFAULT NULL, 173 | `machine_guid` text DEFAULT NULL, 174 | `hit_mine` smallint(6) DEFAULT NULL, 175 | `avoid_mine` smallint(6) DEFAULT NULL, 176 | `checkpoint_miss` smallint(6) DEFAULT NULL, 177 | `miss` smallint(6) DEFAULT NULL, 178 | `w5` smallint(6) DEFAULT NULL, 179 | `w4` smallint(6) DEFAULT NULL, 180 | `w3` smallint(6) DEFAULT NULL, 181 | `w2` smallint(6) DEFAULT NULL, 182 | `w1` smallint(6) DEFAULT NULL, 183 | `checkpoint_hit` smallint(6) DEFAULT NULL, 184 | `let_go` smallint(6) DEFAULT NULL, 185 | `held` smallint(6) DEFAULT NULL, 186 | `missed_hold` smallint(6) DEFAULT NULL, 187 | `stream` decimal(10,6) DEFAULT NULL, 188 | `voltage` decimal(10,6) DEFAULT NULL, 189 | `air` decimal(10,6) DEFAULT NULL, 190 | `freeze` decimal(10,6) DEFAULT NULL, 191 | `chaos` decimal(10,6) DEFAULT NULL, 192 | `notes` smallint(6) DEFAULT NULL, 193 | `taps_holds` smallint(6) DEFAULT NULL, 194 | `jumps` smallint(6) DEFAULT NULL, 195 | `holds` smallint(6) DEFAULT NULL, 196 | `mines` smallint(6) DEFAULT NULL, 197 | `hands` smallint(6) DEFAULT NULL, 198 | `rolls` smallint(6) DEFAULT NULL, 199 | `lifts` smallint(6) DEFAULT NULL, 200 | `fakes` smallint(6) DEFAULT NULL 201 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; 202 | 203 | -- -------------------------------------------------------- 204 | 205 | -- 206 | -- Table structure for table `sm_songs` 207 | -- 208 | 209 | CREATE TABLE `sm_songs` ( 210 | `id` mediumint(9) NOT NULL, 211 | `title` mediumtext DEFAULT NULL, 212 | `subtitle` mediumtext DEFAULT NULL, 213 | `artist` mediumtext DEFAULT NULL, 214 | `pack` mediumtext DEFAULT NULL, 215 | `strippedtitle` mediumtext DEFAULT NULL, 216 | `strippedsubtitle` mediumtext DEFAULT NULL, 217 | `strippedartist` mediumtext DEFAULT NULL, 218 | `song_dir` mediumtext DEFAULT NULL, 219 | `credit` text DEFAULT NULL, 220 | `display_bpm` varchar(50) DEFAULT NULL, 221 | `music_length` decimal(10,0) DEFAULT NULL, 222 | `bga` tinyint(4) DEFAULT NULL, 223 | `installed` tinyint(4) DEFAULT NULL, 224 | `banned` tinyint(4) DEFAULT 0, 225 | `added` datetime DEFAULT NULL, 226 | `checksum` varchar(50) DEFAULT NULL, 227 | `scraper` tinyint(4) DEFAULT NULL 228 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; 229 | 230 | -- -------------------------------------------------------- 231 | 232 | -- 233 | -- Table structure for table `sm_songsplayed` 234 | -- 235 | 236 | CREATE TABLE `sm_songsplayed` ( 237 | `id` int(11) NOT NULL, 238 | `song_id` int(11) DEFAULT NULL, 239 | `song_dir` text DEFAULT NULL, 240 | `request_id` int(11) DEFAULT NULL, 241 | `stepstype` text DEFAULT NULL, 242 | `difficulty` text DEFAULT NULL, 243 | `charthash` VARCHAR(50) DEFAULT NULL, 244 | `username` varchar(50) DEFAULT NULL, 245 | `player_guid` text DEFAULT NULL, 246 | `profile_id` text DEFAULT NULL, 247 | `profile_type` text DEFAULT NULL, 248 | `numplayed` int(11) DEFAULT NULL, 249 | `lastplayed` datetime DEFAULT NULL, 250 | `datetime` datetime DEFAULT NULL 251 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 252 | 253 | -- 254 | -- Indexes for dumped tables 255 | -- 256 | 257 | -- 258 | -- Indexes for table `sm_broadcaster` 259 | -- 260 | ALTER TABLE `sm_broadcaster` 261 | ADD PRIMARY KEY (`id`) USING BTREE; 262 | 263 | -- 264 | -- Indexes for table `sm_notedata` 265 | -- 266 | ALTER TABLE `sm_notedata` 267 | ADD PRIMARY KEY (`id`) USING BTREE, 268 | ADD INDEX `song_id` (`song_id`) USING BTREE; 269 | 270 | -- 271 | -- Indexes for table `sm_requestors` 272 | -- 273 | ALTER TABLE `sm_requestors` 274 | ADD PRIMARY KEY (`id`) USING BTREE; 275 | 276 | -- 277 | -- Indexes for table `sm_requests` 278 | -- 279 | ALTER TABLE `sm_requests` 280 | ADD PRIMARY KEY (`id`) USING BTREE, 281 | ADD INDEX `song_id` (`song_id`) USING BTREE; 282 | 283 | -- 284 | -- Indexes for table `sm_scores` 285 | -- 286 | ALTER TABLE `sm_scores` 287 | ADD PRIMARY KEY (`id`) USING BTREE, 288 | ADD INDEX `song_id` (`song_id`) USING BTREE; 289 | 290 | -- 291 | -- Indexes for table `sm_songs` 292 | -- 293 | ALTER TABLE `sm_songs` 294 | ADD PRIMARY KEY (`id`) USING BTREE; 295 | 296 | -- 297 | -- Indexes for table `sm_songsplayed` 298 | -- 299 | ALTER TABLE `sm_songsplayed` 300 | ADD PRIMARY KEY (`id`) USING BTREE, 301 | ADD INDEX `song_id` (`song_id`) USING BTREE; 302 | 303 | -- 304 | -- AUTO_INCREMENT for dumped tables 305 | -- 306 | 307 | -- 308 | -- AUTO_INCREMENT for table `sm_broadcaster` 309 | -- 310 | ALTER TABLE `sm_broadcaster` 311 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 312 | 313 | -- 314 | -- AUTO_INCREMENT for table `sm_notedata` 315 | -- 316 | ALTER TABLE `sm_notedata` 317 | MODIFY `id` mediumint(9) NOT NULL AUTO_INCREMENT; 318 | 319 | -- 320 | -- AUTO_INCREMENT for table `sm_requestors` 321 | -- 322 | ALTER TABLE `sm_requestors` 323 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 324 | 325 | -- 326 | -- AUTO_INCREMENT for table `sm_requests` 327 | -- 328 | ALTER TABLE `sm_requests` 329 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 330 | 331 | -- 332 | -- AUTO_INCREMENT for table `sm_scores` 333 | -- 334 | ALTER TABLE `sm_scores` 335 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 336 | 337 | -- 338 | -- AUTO_INCREMENT for table `sm_songs` 339 | -- 340 | ALTER TABLE `sm_songs` 341 | MODIFY `id` mediumint(9) NOT NULL AUTO_INCREMENT; 342 | 343 | -- 344 | -- AUTO_INCREMENT for table `sm_songsplayed` 345 | -- 346 | ALTER TABLE `sm_songsplayed` 347 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 348 | COMMIT; 349 | 350 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 351 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 352 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 353 | -------------------------------------------------------------------------------- /request_list/request.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 69 | 70 | //check if the active channel category/game is StepMania, etc. 71 | if(isset($_GET["game"]) && !empty($_GET["game"])){ 72 | $game = mysqli_real_escape_string($conn,$_GET["game"]); 73 | if(in_array(strtolower($game),array_map('strtolower',$categoryGame)) == FALSE){ 74 | die("Hmmm...I don't think it's possible to request songs in ".$game."."); 75 | } 76 | } 77 | 78 | $user = mysqli_real_escape_string($conn,$_GET["user"]); 79 | $tier = mysqli_real_escape_string($conn,$_GET["tier"]); 80 | $twitchid = 0; 81 | if(isset($_GET["userid"])){ 82 | $twitchid = mysqli_real_escape_string($conn,$_GET["userid"]); 83 | } 84 | 85 | //if(empty($_GET["song"]) || empty($_GET["songid"])){ 86 | // die("$user did not specify a song or songID."); 87 | //} 88 | 89 | //get broadcaster 90 | if(isset($_GET["broadcaster"]) && !empty($_GET["broadcaster"])){ 91 | $broadcaster = $_GET["broadcaster"]; 92 | $broadcasterQuery = $broadcaster; 93 | if (isset($_GET["song"]) || isset($_GET["songid"])){ 94 | check_request_toggle($broadcaster, $user); 95 | } 96 | }else{ 97 | $broadcaster = ""; 98 | $broadcasterQuery = "%"; 99 | } 100 | 101 | if(isset($_GET["cancel"])){ 102 | 103 | if (!empty($_GET["cancel"]) && is_numeric($_GET["cancel"]) && $_GET["cancel"] > 0){ 104 | $num = $_GET["cancel"] - 1; 105 | }elseif(empty($_GET["cancel"])){ 106 | $num = 0; 107 | }else{ 108 | die("Good one, ".$user. ", but only positive integers are allowed!"); 109 | } 110 | 111 | $sql = "SELECT * FROM sm_requests WHERE requestor = '{$user}' AND broadcaster LIKE '{$broadcasterQuery}' AND state <> 'canceled' AND state <> 'skipped' AND state <> 'completed' ORDER BY request_time DESC LIMIT 1 OFFSET {$num}"; 112 | $retval = mysqli_query( $conn, $sql ); 113 | 114 | if (mysqli_num_rows($retval) == 1) { 115 | while($row = mysqli_fetch_assoc($retval)) { 116 | 117 | $request_id = $row["id"]; 118 | $song_id = $row["song_id"]; 119 | 120 | $sql2 = "SELECT * FROM sm_songs WHERE id = '{$song_id}' LIMIT 1"; 121 | $retval2 = mysqli_query( $conn, $sql2 ); 122 | while($row2 = mysqli_fetch_assoc($retval2)){ 123 | $sql3 = "UPDATE sm_requests SET state = 'canceled' WHERE id = '{$request_id}'"; 124 | $retval3 = mysqli_query( $conn, $sql3 ); 125 | echo "Canceled {$user}'s request for ".trim($row2["title"]." ".$row2["subtitle"]); 126 | } 127 | } 128 | 129 | }else{ 130 | echo "$user hasn't requested any songs!"; 131 | } 132 | 133 | die(); 134 | } 135 | 136 | if(isset($_GET["skip"])){ 137 | 138 | if (!empty($_GET["skip"]) && is_numeric($_GET["skip"]) && $_GET["skip"] > 0){ 139 | $num = $_GET["skip"] - 1; 140 | }elseif(empty($_GET["skip"])){ 141 | $num = 0; 142 | }else{ 143 | die("Good one, ".$user. ", but only positive integers are allowed!"); 144 | } 145 | 146 | $sql = "SELECT * FROM sm_requests WHERE broadcaster LIKE '{$broadcasterQuery}' AND state <> 'canceled' AND state <> 'skipped' AND state <> 'completed' ORDER BY request_time DESC LIMIT 1 OFFSET {$num}"; 147 | $retval = mysqli_query( $conn, $sql ); 148 | 149 | while($row = mysqli_fetch_assoc($retval)) { 150 | $request_id = $row["id"]; 151 | $song_id = $row["song_id"]; 152 | $sql2 = "SELECT * FROM sm_songs WHERE id = \"$song_id\" LIMIT 1"; 153 | $retval2 = mysqli_query( $conn, $sql2 ); 154 | while($row2 = mysqli_fetch_assoc($retval2)){ 155 | $sql3 = "UPDATE sm_requests SET state=\"skipped\" WHERE id = \"$request_id\""; 156 | $retval3 = mysqli_query( $conn, $sql3 ); 157 | echo "$user skipped ".trim($row2["title"]." ".$row2["subtitle"]); 158 | } 159 | } 160 | 161 | die(); 162 | } 163 | 164 | if(isset($_GET["complete"])){ 165 | 166 | if (!empty($_GET["complete"]) && is_numeric($_GET["complete"]) && $_GET["complete"] > 0){ 167 | $num = $_GET["complete"] - 1; 168 | }elseif(empty($_GET["complete"])){ 169 | $num = 0; 170 | }else{ 171 | die("Good one, ".$user. ", but only positive integers are allowed!"); 172 | } 173 | 174 | $sql = "SELECT * FROM sm_requests WHERE broadcaster LIKE '{$broadcasterQuery}' AND state <> 'canceled' AND state <> 'skipped' AND state <> 'requested' ORDER BY request_time DESC LIMIT 1 OFFSET {$num}"; 175 | $retval = mysqli_query( $conn, $sql ); 176 | 177 | while($row = mysqli_fetch_assoc($retval)) { 178 | $request_id = $row["id"]; 179 | $song_id = $row["song_id"]; 180 | $sql2 = "SELECT * FROM sm_songs WHERE id = \"$song_id\" LIMIT 1"; 181 | $retval2 = mysqli_query( $conn, $sql2 ); 182 | while($row2 = mysqli_fetch_assoc($retval2)){ 183 | $sql3 = "UPDATE sm_requests SET state=\"completed\" WHERE id = \"$request_id\""; 184 | $retval3 = mysqli_query( $conn, $sql3 ); 185 | echo "$user completed ".trim($row2["title"]." ".$row2["subtitle"]); 186 | } 187 | } 188 | 189 | die(); 190 | } 191 | 192 | if(isset($_GET["songid"]) && !empty($_GET["songid"])){ 193 | $commandArgs = parseCommandArgs($_GET["songid"],$user,$broadcaster); 194 | 195 | if(empty($commandArgs["song"])){ 196 | echo "$user didn't specify a song ID!"; 197 | die(); 198 | } 199 | 200 | //clean up song ID 201 | $song = clean($commandArgs["song"]); 202 | $song = preg_replace('/\D/','',$song); 203 | if(!is_numeric($song) || empty($song)){ 204 | echo "$user gave an invalid song ID!"; 205 | die(); 206 | } 207 | //lookup by ID and request it 208 | 209 | $sql = "SELECT * FROM sm_songs WHERE id = '{$song}' AND installed=1 ORDER BY title ASC, pack ASC"; 210 | $retval = mysqli_query( $conn, $sql ); 211 | 212 | if (mysqli_num_rows($retval) == 1) { 213 | while($row = mysqli_fetch_assoc($retval)) { 214 | request_song($row["id"], $user, $tier, $twitchid, $broadcaster, $commandArgs); 215 | $displayModeDiff = display_ModeDiff($commandArgs); 216 | $displayArtist = get_duplicate_song_artist ($row["id"]); 217 | echo "$user requested " . trim($row["title"]." ".$row["subtitle"]). $displayArtist . " from " . $row["pack"].$displayModeDiff; 218 | die(); 219 | } 220 | } else { 221 | echo "$user => Didn't find any songs matching the ID: " . $song . "!"; 222 | die(); 223 | } 224 | 225 | die(); 226 | } 227 | 228 | if(isset($_GET["song"]) && !empty($_GET["song"])){ 229 | $commandArgs = parseCommandArgs($_GET["song"],$user,$broadcaster); 230 | 231 | if(empty($commandArgs["song"])){ 232 | echo "$user didn't specify a song name!"; 233 | die(); 234 | } 235 | 236 | $song = $commandArgs["song"]; 237 | 238 | //easter egg requests 239 | $song = is_emote_request($song); 240 | //process & clean song 241 | $song = clean($song); 242 | 243 | //Determine if there's a song with this exact title. If someone requested "Tsugaru", this would match "TSUGARU" but would not match "TSUGARU (Apple Mix)" 244 | $sql = "SELECT * FROM sm_songs WHERE (IF(strippedsubtitle is NULL OR strippedsubtitle='',strippedtitle,CONCAT(strippedtitle,'-',strippedsubtitle))=\"$song\" OR strippedtitle=\"$song\") AND installed = 1 ORDER BY title ASC, pack ASC"; 245 | $retval = mysqli_query( $conn, $sql ); 246 | 247 | if (mysqli_num_rows($retval) == 1) { 248 | while($row = mysqli_fetch_assoc($retval)) { 249 | request_song($row["id"], $user, $tier, $twitchid, $broadcaster, $commandArgs); 250 | $displayModeDiff = display_ModeDiff($commandArgs); 251 | $displayArtist = get_duplicate_song_artist ($row["id"]); 252 | echo "$user requested " . trim($row["title"]." ".$row["subtitle"]). $displayArtist . " from " . $row["pack"].$displayModeDiff;; 253 | } 254 | die(); 255 | //end exact match 256 | } 257 | 258 | $sql = "SELECT sm_songs.id AS id,sm_songs.title AS title,sm_songs.subtitle AS subtitle,sm_songs.pack AS pack FROM sm_songs 259 | LEFT JOIN sm_songsplayed ON sm_songs.id = sm_songsplayed.song_id 260 | WHERE (IF(strippedsubtitle is NULL OR strippedsubtitle='',strippedtitle,CONCAT(strippedtitle,'-',strippedsubtitle)) LIKE \"%$song%\" OR strippedtitle LIKE \"%$song%\") AND installed = 1 261 | GROUP BY sm_songs.id 262 | ORDER BY SUM(sm_songsplayed.numplayed) DESC, title ASC, pack ASC"; 263 | $retval = mysqli_query( $conn, $sql ); 264 | 265 | if (mysqli_num_rows($retval) == 1) { 266 | while($row = mysqli_fetch_assoc($retval)) { 267 | request_song($row["id"], $user, $tier, $twitchid, $broadcaster, $commandArgs); 268 | $displayModeDiff = display_ModeDiff($commandArgs); 269 | $displayArtist = get_duplicate_song_artist ($row["id"]); 270 | echo "$user requested " . trim($row["title"]." ".$row["subtitle"]). $displayArtist . " from " . $row["pack"].$displayModeDiff;; 271 | } 272 | die(); 273 | //end one match 274 | } 275 | //no one match 276 | if (mysqli_num_rows($retval) > 0) { 277 | echo "$user => Top matches (request with !requestid [song id]):"; 278 | $i=1; 279 | while($row = mysqli_fetch_assoc($retval)) { 280 | if($i>4){die();} 281 | $displayArtist = get_duplicate_song_artist ($row["id"]); 282 | echo " [ ".$row["id"]. " -> " .trim($row["title"]." ".$row["subtitle"]).$displayArtist." from ".$row["pack"]." ] "; 283 | $i++; 284 | } 285 | } elseif (is_numeric($song)) { 286 | echo "$user => Did you mean to use !requestid ".$song."?"; 287 | }else{ 288 | echo "$user => Didn't find any songs matching that name! Check the !songlist."; 289 | } 290 | 291 | die(); 292 | } 293 | 294 | mysqli_close($conn); 295 | die(); 296 | ?> 297 | -------------------------------------------------------------------------------- /client_scrapers/upload_banners.php: -------------------------------------------------------------------------------- 1 | 0){ 63 | $ext = implode(', ',$missingExt); 64 | wh_log("ERROR: $ext extension(s) not enabled. Please enable the extension(s) in your PHP config file: \"$iniPath\""); 65 | die("$ext extension(s) not enabled. Please enable the extension(s) in your PHP config file: \"$iniPath\"" . PHP_EOL); 66 | } 67 | } 68 | 69 | function wh_log($log_msg){ 70 | $log_filename = __DIR__."/log"; 71 | if (!file_exists($log_filename)) 72 | { 73 | // create directory/folder uploads. 74 | mkdir($log_filename, 0777, true); 75 | } 76 | $log_file_data = $log_filename.'/log_' . date('Y-m-d') . '.log'; 77 | $log_msg = rtrim($log_msg); //remove line endings 78 | // if you don't add `FILE_APPEND`, the file will be erased each time you add a log 79 | file_put_contents($log_file_data, date("Y-m-d H:i:s") . " -- [" . strtoupper(basename(__FILE__)) . "] : ". $log_msg . PHP_EOL, FILE_APPEND); 80 | } 81 | 82 | function get_version(){ 83 | //check the version of this script against the server 84 | $versionFilename = __DIR__."/VERSION"; 85 | 86 | if(file_exists($versionFilename)){ 87 | $versionClient = file_get_contents($versionFilename); 88 | $versionClient = json_decode($versionClient,TRUE); 89 | $versionClient = $versionClient['version']; 90 | 91 | // if($versionServer > $versionClient){ 92 | // wh_log("Script out of date. Client: ".$versionClient." | Server: ".$versionServer); 93 | // die("WARNING! Your client scripts are out of date! Update your scripts to the latest version! Exiting..." . PHP_EOL); 94 | // } 95 | }else{ 96 | $versionClient = 0; 97 | wh_log("Client version not found or unexpected value. Check VERSION file in client scrapers folder."); 98 | } 99 | return $versionClient; 100 | } 101 | 102 | function check_target_url(){ 103 | global $targetURL; 104 | global $target_url; 105 | 106 | if(isset($target_url) && !empty($target_url)){ 107 | $targetURL = $target_url; 108 | } 109 | if(!isset($targetURL) || empty($targetURL)){ 110 | die("No target URL found! Check the \"targetURL\" value in your config.php file" . PHP_EOL); 111 | }elseif(filter_var($targetURL,FILTER_VALIDATE_URL) === FALSE){ 112 | die("\"$targetURL\" is not a valid URL. Check the \"targetURL\" value in your config.php file" . PHP_EOL); 113 | }elseif(preg_match('/(smrequests\.)(com|dev)/',$targetURL)){ 114 | //this is a hosted domain 115 | if(!preg_match('/(https:\/\/.+\.smrequests\.)(com|dev)(?!\/)/',$targetURL)){ 116 | die("\"$targetURL\" is not a valid URL for the SMRequests hosted service. Check the \"targetURL\" value in your config.php file" . PHP_EOL); 117 | } 118 | } 119 | } 120 | 121 | function findFiles($directory) { 122 | //find all directories in a directory and sort by modified time 123 | $dir_paths = array (); 124 | foreach(glob("{$directory}/*", GLOB_ONLYDIR) as $filename) { 125 | $dir_paths[] = $filename; 126 | } 127 | usort( $dir_paths, function( $a, $b ) { return filemtime($b) - filemtime($a); } ); 128 | return $dir_paths; 129 | } 130 | 131 | function isIgnoredPack($pack){ 132 | global $packsIgnore; 133 | global $packsIgnoreRegex; 134 | 135 | $return = FALSE; 136 | if(!is_array($packsIgnore)){ 137 | $packsIgnore = array($packsIgnore); 138 | } 139 | if (in_array($pack,$packsIgnore)){ 140 | $return = TRUE; 141 | }elseif(!empty($packsIgnoreRegex)){ 142 | if(preg_match($packsIgnoreRegex,$pack)){ 143 | $return = TRUE; 144 | } 145 | } 146 | return $return; 147 | } 148 | 149 | function get_banner($img_path){ 150 | //look for banners, reject known not banners 151 | foreach($img_path as $img){ 152 | $filename = pathinfo($img,PATHINFO_FILENAME); 153 | if(stripos($filename,'banner') !== FALSE){ 154 | $return = $img; 155 | break; 156 | }elseif(stripos($filename,'bn') !== FALSE){ 157 | $return = $img; 158 | break; 159 | }elseif(stripos($filename,'ban') !== FALSE){ 160 | $return = $img; 161 | break; 162 | }elseif(stripos($filename,'jacket') !== FALSE){ 163 | continue; 164 | }elseif(stripos($filename,'cdtitle') !== FALSE){ 165 | continue; 166 | }else{ 167 | $return = $img; 168 | } 169 | } 170 | return $return; 171 | } 172 | 173 | function does_banner_exist($file,$pack_name){ 174 | //quick check to see if the banner is on the server 175 | global $targetURL; 176 | $return = FALSE; 177 | unset($ch); 178 | 179 | $imgName = urlencode($pack_name.'.'.strtolower(pathinfo($file,PATHINFO_EXTENSION))); 180 | $ch = curl_init($targetURL."/images/packs/".$imgName); 181 | curl_setopt($ch, CURLOPT_NOBODY, TRUE); 182 | curl_exec($ch); 183 | $retcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 184 | curl_close($ch); 185 | if($retcode == 200){$return = TRUE;} 186 | return $return; 187 | } 188 | 189 | function curl_upload($file,$pack_name){ 190 | global $targetURL; 191 | global $security_key; 192 | unset($ch,$post,$cFile); 193 | $versionClient = get_version(); 194 | //add the security_key to the http header 195 | if(!isset($security_key) || empty($security_key)){ 196 | die("No security_key found! Check the \"security_key\" value in your config.php file" . PHP_EOL); 197 | } 198 | $security_keyToken = base64_encode($security_key); 199 | //special curl function to create the information needed to upload files 200 | //renaming the banner images to be consistent with the pack name 201 | $cFile = curl_file_create($file,'',$pack_name.'.'.strtolower(pathinfo($file,PATHINFO_EXTENSION))); 202 | //add the security_key to the array 203 | $post = array('version' => $versionClient, 'file_contents'=> $cFile); 204 | //this curl method only works with PHP 5.5+ 205 | $ch = curl_init(); 206 | curl_setopt($ch, CURLOPT_URL,$targetURL."/banners.php"); 207 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 208 | curl_setopt($ch, CURLOPT_HTTPHEADER, array("Key: $security_keyToken")); 209 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //if true, must specify cacert.pem location in php.ini 210 | curl_setopt($ch, CURLOPT_ENCODING,'gzip,deflate'); 211 | curl_setopt($ch, CURLOPT_POST,1); 212 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 213 | curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); 214 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 215 | $result = curl_exec ($ch); 216 | if($result === FALSE){ 217 | echo "Curl error: ".curl_error($ch) . PHP_EOL; 218 | wh_log("Curl error: ".curl_error($ch)); 219 | } 220 | if(curl_getinfo($ch, CURLINFO_HTTP_CODE) < 400){ 221 | //good response from the server 222 | echo $result; //echo from the server-side script 223 | wh_log($result); 224 | $return = 0; 225 | }else{ 226 | //some kind of error 227 | echo "There was an error communicating with $targetURL." . PHP_EOL; 228 | wh_log("The server responded with error: " . curl_getinfo($ch, CURLINFO_HTTP_CODE)); 229 | echo "The server responded with error: " . curl_getinfo($ch, CURLINFO_HTTP_CODE) . PHP_EOL; 230 | $return = 1; 231 | } 232 | curl_close ($ch); 233 | return $return; 234 | } 235 | 236 | //check php environment 237 | check_environment(); 238 | 239 | //check for valid target URL 240 | check_target_url(); 241 | 242 | echo "Finding and uploading pack banner images..." . PHP_EOL; 243 | 244 | //ready variables 245 | $banners_copied = $notFoundBanners = $cPacks = 0; 246 | $fileSizeMax = 5242880; //5MB 247 | 248 | // find all the pack/group folders 249 | $pack_dir = findFiles($songsDir); 250 | 251 | //add any additional songs folder(s) 252 | if(!empty($addSongsDir)){ 253 | if(!is_array($addSongsDir)){ 254 | $addSongsDir = array($addSongsDir); 255 | } 256 | foreach($addSongsDir as $directory){ 257 | $pack_dir[] = findFiles($directory); 258 | } 259 | } 260 | 261 | $cPacks = count($pack_dir); 262 | 263 | if ($cPacks == 0){ 264 | wh_log("No pack/group folders found. Your StepMania /Songs directory may be located in \"AppData\""); 265 | die ("No pack/group folders found. Your StepMania /Songs directory may be located in \"AppData\"" . PHP_EOL); 266 | } 267 | 268 | $img_arr = array(); 269 | 270 | foreach ($pack_dir as $path){ 271 | 272 | $pack_name = $img_path = ""; 273 | //get pack name from folder 274 | $pack_name = substr($path,strrpos($path,"/")+1); 275 | //check if pack is to be ignored and skip if it is 276 | if(!isIgnoredPack($pack_name)){ 277 | //pack is not ignored 278 | //clean up pack name and replace spaces with underscore 279 | $pack_name = strtolower(preg_replace('/\s+/', '_', trim($pack_name))); 280 | //look for any picture file in the pack directory 281 | $img_path = glob("{$path}/*{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF,bmp,BMP}",GLOB_BRACE); 282 | 283 | if (isset($img_path) && !empty($img_path)){ 284 | if(count($img_path) > 1){ 285 | //more than 1 image found, let's search file names for which one is the banner 286 | $img_path = get_banner($img_path); 287 | }else{ 288 | //use the first result as the pack banner 289 | $img_path = $img_path[0]; 290 | } 291 | //check for filesize 292 | if (filesize($img_path) > $fileSizeMax){ 293 | echo $pack_name."'s image file is too large (max size: ". $fileSizeMax / 1024^2 ."MB)!" . PHP_EOL; 294 | wh_log($pack_name."'s image file is too large (max size: ". $fileSizeMax / 1024^2 ."MB)!"); 295 | }else{ 296 | $img_arr[] = array('img_path' => $img_path,'pack_name' => $pack_name); 297 | } 298 | }else{ 299 | echo "No banner image for ".$pack_name. PHP_EOL; 300 | wh_log("No banner image for ".$pack_name); 301 | $notFoundBanners++; 302 | } 303 | } 304 | } 305 | 306 | foreach ($img_arr as $img){ 307 | //check if banner already on server 308 | //if(does_banner_exist($img['img_path'],$img['pack_name'])){ 309 | // echo "Banner for ". $img['pack_name'] . " already exists. Skipping...".PHP_EOL; 310 | //}else{ 311 | //upload banner images 312 | if(curl_upload($img['img_path'],$img['pack_name']) === 0){ 313 | $banners_copied++; 314 | } 315 | //} 316 | } 317 | 318 | $cPacks = $cPacks - $notFoundBanners; 319 | 320 | //STATS! 321 | echo "Uploaded ".$banners_copied." of ".$cPacks." banner images. Banners were not found for ".$notFoundBanners." packs." . PHP_EOL; 322 | wh_log("Uploaded ".$banners_copied." of ".$cPacks." banner images. Banners were not found for ".$notFoundBanners." packs."); 323 | 324 | exit(); 325 | ?> 326 | -------------------------------------------------------------------------------- /chatbot/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatbot", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "6.12.4", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", 10 | "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", 11 | "requires": { 12 | "fast-deep-equal": "^3.1.1", 13 | "fast-json-stable-stringify": "^2.0.0", 14 | "json-schema-traverse": "^0.4.1", 15 | "uri-js": "^4.2.2" 16 | } 17 | }, 18 | "asn1": { 19 | "version": "0.2.4", 20 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 21 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 22 | "requires": { 23 | "safer-buffer": "~2.1.0" 24 | } 25 | }, 26 | "assert-plus": { 27 | "version": "1.0.0", 28 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 29 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 30 | }, 31 | "async-limiter": { 32 | "version": "1.0.1", 33 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 34 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" 35 | }, 36 | "asynckit": { 37 | "version": "0.4.0", 38 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 39 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 40 | }, 41 | "aws-sign2": { 42 | "version": "0.7.0", 43 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 44 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 45 | }, 46 | "aws4": { 47 | "version": "1.10.1", 48 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", 49 | "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" 50 | }, 51 | "bcrypt-pbkdf": { 52 | "version": "1.0.2", 53 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 54 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 55 | "requires": { 56 | "tweetnacl": "^0.14.3" 57 | } 58 | }, 59 | "caseless": { 60 | "version": "0.12.0", 61 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 62 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 63 | }, 64 | "combined-stream": { 65 | "version": "1.0.8", 66 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 67 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 68 | "requires": { 69 | "delayed-stream": "~1.0.0" 70 | } 71 | }, 72 | "core-util-is": { 73 | "version": "1.0.2", 74 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 75 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 76 | }, 77 | "dashdash": { 78 | "version": "1.14.1", 79 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 80 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 81 | "requires": { 82 | "assert-plus": "^1.0.0" 83 | } 84 | }, 85 | "delayed-stream": { 86 | "version": "1.0.0", 87 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 88 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 89 | }, 90 | "dotenv": { 91 | "version": "8.2.0", 92 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 93 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 94 | }, 95 | "ecc-jsbn": { 96 | "version": "0.1.2", 97 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 98 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 99 | "requires": { 100 | "jsbn": "~0.1.0", 101 | "safer-buffer": "^2.1.0" 102 | } 103 | }, 104 | "extend": { 105 | "version": "3.0.2", 106 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 107 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 108 | }, 109 | "extsprintf": { 110 | "version": "1.3.0", 111 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 112 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 113 | }, 114 | "fast-deep-equal": { 115 | "version": "3.1.3", 116 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 117 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 118 | }, 119 | "fast-json-stable-stringify": { 120 | "version": "2.1.0", 121 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 122 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 123 | }, 124 | "forever-agent": { 125 | "version": "0.6.1", 126 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 127 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 128 | }, 129 | "form-data": { 130 | "version": "2.3.3", 131 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 132 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 133 | "requires": { 134 | "asynckit": "^0.4.0", 135 | "combined-stream": "^1.0.6", 136 | "mime-types": "^2.1.12" 137 | } 138 | }, 139 | "getpass": { 140 | "version": "0.1.7", 141 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 142 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 143 | "requires": { 144 | "assert-plus": "^1.0.0" 145 | } 146 | }, 147 | "har-schema": { 148 | "version": "2.0.0", 149 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 150 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 151 | }, 152 | "har-validator": { 153 | "version": "5.1.5", 154 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", 155 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", 156 | "requires": { 157 | "ajv": "^6.12.3", 158 | "har-schema": "^2.0.0" 159 | } 160 | }, 161 | "http-signature": { 162 | "version": "1.2.0", 163 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 164 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 165 | "requires": { 166 | "assert-plus": "^1.0.0", 167 | "jsprim": "^1.2.2", 168 | "sshpk": "^1.7.0" 169 | } 170 | }, 171 | "is-typedarray": { 172 | "version": "1.0.0", 173 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 174 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 175 | }, 176 | "isstream": { 177 | "version": "0.1.2", 178 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 179 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 180 | }, 181 | "jsbn": { 182 | "version": "0.1.1", 183 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 184 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 185 | }, 186 | "json-schema": { 187 | "version": "0.2.3", 188 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 189 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 190 | }, 191 | "json-schema-traverse": { 192 | "version": "0.4.1", 193 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 194 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 195 | }, 196 | "json-stringify-safe": { 197 | "version": "5.0.1", 198 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 199 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 200 | }, 201 | "jsprim": { 202 | "version": "1.4.1", 203 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 204 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 205 | "requires": { 206 | "assert-plus": "1.0.0", 207 | "extsprintf": "1.3.0", 208 | "json-schema": "0.2.3", 209 | "verror": "1.10.0" 210 | } 211 | }, 212 | "mime-db": { 213 | "version": "1.44.0", 214 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 215 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 216 | }, 217 | "mime-types": { 218 | "version": "2.1.27", 219 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 220 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 221 | "requires": { 222 | "mime-db": "1.44.0" 223 | } 224 | }, 225 | "oauth-sign": { 226 | "version": "0.9.0", 227 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 228 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 229 | }, 230 | "performance-now": { 231 | "version": "2.1.0", 232 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 233 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 234 | }, 235 | "psl": { 236 | "version": "1.8.0", 237 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 238 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" 239 | }, 240 | "punycode": { 241 | "version": "2.1.1", 242 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 243 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 244 | }, 245 | "qs": { 246 | "version": "6.5.2", 247 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 248 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 249 | }, 250 | "request": { 251 | "version": "2.88.0", 252 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 253 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 254 | "requires": { 255 | "aws-sign2": "~0.7.0", 256 | "aws4": "^1.8.0", 257 | "caseless": "~0.12.0", 258 | "combined-stream": "~1.0.6", 259 | "extend": "~3.0.2", 260 | "forever-agent": "~0.6.1", 261 | "form-data": "~2.3.2", 262 | "har-validator": "~5.1.0", 263 | "http-signature": "~1.2.0", 264 | "is-typedarray": "~1.0.0", 265 | "isstream": "~0.1.2", 266 | "json-stringify-safe": "~5.0.1", 267 | "mime-types": "~2.1.19", 268 | "oauth-sign": "~0.9.0", 269 | "performance-now": "^2.1.0", 270 | "qs": "~6.5.2", 271 | "safe-buffer": "^5.1.2", 272 | "tough-cookie": "~2.4.3", 273 | "tunnel-agent": "^0.6.0", 274 | "uuid": "^3.3.2" 275 | } 276 | }, 277 | "safe-buffer": { 278 | "version": "5.2.1", 279 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 280 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 281 | }, 282 | "safer-buffer": { 283 | "version": "2.1.2", 284 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 285 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 286 | }, 287 | "sshpk": { 288 | "version": "1.16.1", 289 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 290 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 291 | "requires": { 292 | "asn1": "~0.2.3", 293 | "assert-plus": "^1.0.0", 294 | "bcrypt-pbkdf": "^1.0.0", 295 | "dashdash": "^1.12.0", 296 | "ecc-jsbn": "~0.1.1", 297 | "getpass": "^0.1.1", 298 | "jsbn": "~0.1.0", 299 | "safer-buffer": "^2.0.2", 300 | "tweetnacl": "~0.14.0" 301 | } 302 | }, 303 | "tmi.js": { 304 | "version": "1.5.0", 305 | "resolved": "https://registry.npmjs.org/tmi.js/-/tmi.js-1.5.0.tgz", 306 | "integrity": "sha512-JyWKy9dRkZDG1h6PnpE8fJVsTrW82/yANXoP7R3u02vG7PLCvHGRGTWzBwk0ymMJGX9A+YzDx5tXQDsTeJd/5A==", 307 | "requires": { 308 | "request": "2.88.0", 309 | "ws": "6.1.3" 310 | } 311 | }, 312 | "tough-cookie": { 313 | "version": "2.4.3", 314 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 315 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 316 | "requires": { 317 | "psl": "^1.1.24", 318 | "punycode": "^1.4.1" 319 | }, 320 | "dependencies": { 321 | "punycode": { 322 | "version": "1.4.1", 323 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 324 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 325 | } 326 | } 327 | }, 328 | "tunnel-agent": { 329 | "version": "0.6.0", 330 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 331 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 332 | "requires": { 333 | "safe-buffer": "^5.0.1" 334 | } 335 | }, 336 | "tweetnacl": { 337 | "version": "0.14.5", 338 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 339 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 340 | }, 341 | "uri-js": { 342 | "version": "4.2.2", 343 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 344 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 345 | "requires": { 346 | "punycode": "^2.1.0" 347 | } 348 | }, 349 | "uuid": { 350 | "version": "3.4.0", 351 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 352 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 353 | }, 354 | "verror": { 355 | "version": "1.10.0", 356 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 357 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 358 | "requires": { 359 | "assert-plus": "^1.0.0", 360 | "core-util-is": "1.0.2", 361 | "extsprintf": "^1.2.0" 362 | } 363 | }, 364 | "ws": { 365 | "version": "6.1.3", 366 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz", 367 | "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==", 368 | "requires": { 369 | "async-limiter": "~1.0.0" 370 | } 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /request_list/misc_functions.php: -------------------------------------------------------------------------------- 1 | set_charset("utf8mb4"); 11 | 12 | function clean($string) { 13 | $string = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $string); //tranliterate 14 | $string = preg_replace('/ +/', '-', $string); // Replaces all spaces with hyphens. 15 | $string = preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars. 16 | return $string; 17 | } 18 | 19 | function add_user($userid, $user){ 20 | 21 | global $conn; 22 | 23 | $user = strtolower($user); 24 | $sql = "INSERT INTO sm_requestors (twitchid, name, dateadded) VALUES ('$userid', '$user', NOW())"; 25 | $retval = mysqli_query( $conn, $sql ); 26 | $the_id = mysqli_insert_id($conn); 27 | 28 | return($the_id); 29 | 30 | } 31 | 32 | function check_user($userid, $user){ 33 | 34 | global $conn; 35 | 36 | $user = strtolower($user); 37 | 38 | //case where the bot cannot supply the twitchid, use the name 39 | if($userid > 0 || !empty($userid)){ 40 | $sql0 = "SELECT * FROM sm_requestors WHERE twitchid = '$userid'"; 41 | }else{ 42 | $sql0 = "SELECT * FROM sm_requestors WHERE name = '$user'"; 43 | } 44 | $retval0 = mysqli_query( $conn, $sql0 ); 45 | $numrows = mysqli_num_rows($retval0); 46 | 47 | if($numrows != 0){ 48 | //User exists in DB, return data 49 | $row0 = mysqli_fetch_assoc($retval0); 50 | $id = $row0["id"]; 51 | $twitchid = $row0["twitchid"]; 52 | $whitelisted = $row0["whitelisted"]; 53 | $banned = $row0["banned"]; 54 | }else{ 55 | //User is new - create then return data 56 | $id = add_user($userid, $user); 57 | $whitelisted = "false"; 58 | $banned = "false"; 59 | } 60 | 61 | $userobj["id"] = "$id"; 62 | $userobj["name"] = "$user"; 63 | $userobj["twitchid"] = "$userid"; 64 | $userobj["whitelisted"] = "$whitelisted"; 65 | $userobj["banned"] = "$banned"; 66 | 67 | return($userobj); 68 | 69 | } 70 | 71 | function check_length($maxRequests){ 72 | global $conn; 73 | if(!isset($maxRequests) || !is_numeric($maxRequests)){ 74 | $maxRequests = 10; 75 | } 76 | $sql0 = "SELECT state FROM sm_requests ORDER BY request_time DESC LIMIT $maxRequests"; 77 | $retval0 = mysqli_query( $conn, $sql0 ); 78 | $length = 0; 79 | foreach($retval0 as $row){ 80 | if($row['state'] == 'requested'){ 81 | $length++; 82 | } 83 | } 84 | if($length > $maxRequests){ 85 | die("Too many songs on the request list! Try again in a few minutes."); 86 | } 87 | return $length; 88 | } 89 | 90 | function check_cooldown($user){ 91 | global $cooldownMultiplier; 92 | global $maxRequests; 93 | 94 | //check config variables 95 | if(empty($cooldownMultiplier) || !is_numeric($cooldownMultiplier)){ 96 | $cooldownMultiplier = 0.4; 97 | } 98 | if(empty($maxRequests) || !is_numeric($maxRequests)){ 99 | $maxRequests = 10; 100 | } 101 | 102 | //check total length of requests, if over maxRequests, stop 103 | $length = check_length($maxRequests); 104 | 105 | $interval = $cooldownMultiplier * $length; 106 | 107 | //scale cooldown as a function of the number of requests. X minutes per open request. 108 | global $conn; 109 | $sql0 = "SELECT * FROM sm_requests WHERE state <> 'canceled' AND requestor = '$user' AND request_time > DATE_SUB(NOW(), INTERVAL {$interval} MINUTE)"; 110 | $retval0 = mysqli_query( $conn, $sql0 ); 111 | $numrows = mysqli_num_rows($retval0); 112 | if($numrows > 0 && floor($interval) > 1){ 113 | die("Slow down there, part'ner! Try again in ".floor($interval)." minutes."); 114 | }elseif($numrows > 0 && floor($interval) <= 1){ 115 | die("Slow down there, part'ner! Try again in 1 minute."); 116 | } 117 | } 118 | 119 | function requested_recently($song_id,$requestor,$whitelisted,$interval = 1){ 120 | global $conn; 121 | 122 | if(empty($interval) || !is_numeric($interval)){$interval = 1;} 123 | $sql0 = "SELECT COUNT(*) AS total 124 | FROM sm_requests 125 | WHERE song_id = '$song_id' AND (state = 'requested' OR state = 'completed') AND request_time > DATE_SUB(NOW(), INTERVAL $interval HOUR)"; 126 | $retval0 = mysqli_query( $conn, $sql0 ); 127 | $row0 = mysqli_fetch_assoc($retval0); 128 | 129 | if($row0["total"] > 0){ 130 | //if(($row0["total"] > 0) && ($whitelisted != "true")){ 131 | die("$requestor => This song has already been requested recently!"); 132 | } 133 | } 134 | 135 | function recently_played($song_id,$interval){ 136 | global $conn; 137 | $recently_played = FALSE; 138 | if(empty($interval) || !is_numeric($interval)){$interval = 1;} 139 | $sql = "SELECT song_id FROM sm_songsplayed WHERE song_id={$song_id} AND lastplayed > DATE_SUB(NOW(), INTERVAL $interval HOUR)"; 140 | $retval = mysqli_query($conn,$sql); 141 | if(mysqli_num_rows($retval) > 0){ 142 | $recently_played = TRUE; 143 | } 144 | return $recently_played; 145 | } 146 | 147 | function is_emote_request($song){ 148 | $emoteArray = array ( 149 | "mrtwin1HaHaHa" => "The Smiler", 150 | "danizoOHNO" => "Minna no Kimochi", 151 | "beniplKitty" => "Kitty From Hell", 152 | "ddrDav2" => "Heaven is a 57 metallic gray", 153 | "hellki1Nabi1" => "My Baby Mama", 154 | "iambgsKool" => "New Horizons TOKYO", 155 | "kikoiaRusty" => "Doom Crossing", 156 | "noreseSLOW" => "VEAH", 157 | "xancarDex" => "Ding Dong Song" 158 | ); 159 | 160 | if(array_key_exists($song,$emoteArray)){ 161 | $song = $emoteArray[$song]; 162 | } 163 | 164 | return $song; 165 | } 166 | 167 | function get_broadcaster_limits($broadcaster){ 168 | global $conn; 169 | $broadcaserLimits = array(); 170 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster = '$broadcaster'"; 171 | $retval0 = mysqli_query( $conn, $sql0 ); 172 | 173 | if(mysqli_num_rows($retval0) == 1){ 174 | $broadcaserLimits = mysqli_fetch_assoc($retval0); 175 | } 176 | return $broadcaserLimits; 177 | } 178 | 179 | function check_request_toggle($broadcaster,$user = NULL){ 180 | global $conn; 181 | 182 | if(strtolower($broadcaster) == strtolower($user)){ 183 | //requestor is broadcaster: bypass 184 | return; 185 | } 186 | 187 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster LIKE '$broadcaster'"; 188 | $retval0 = mysqli_query( $conn, $sql0 ); 189 | $numrows = mysqli_num_rows($retval0); 190 | 191 | if($numrows == 1){ 192 | $row0 = mysqli_fetch_assoc($retval0); 193 | $request_toggle = $row0["request_toggle"]; 194 | $message = $row0["message"]; 195 | if($request_toggle == "OFF"){ 196 | $requestsDisableResponses = array("Requests are off.","Requests are disabled.","Requests are deactivated."); 197 | $response = $requestsDisableResponses[array_rand($requestsDisableResponses,1)]; 198 | die($response . " " . $message); 199 | } 200 | } 201 | } 202 | 203 | function check_stepstype($broadcaster,$song_id){ 204 | global $conn; 205 | 206 | $response = TRUE; 207 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster LIKE '$broadcaster'"; 208 | $retval0 = mysqli_query( $conn, $sql0 ); 209 | $numrows = mysqli_num_rows($retval0); 210 | 211 | if($numrows == 1){ 212 | $row0 = mysqli_fetch_assoc($retval0); 213 | $stepstype = $row0["stepstype"]; 214 | if(!empty($stepstype)){ 215 | $sql = "SELECT * FROM sm_notedata WHERE song_id = '$song_id' AND stepstype LIKE '$stepstype'"; 216 | $retval = mysqli_query( $conn, $sql); 217 | if(mysqli_num_rows($retval) == 0){ 218 | $response = FALSE; 219 | } 220 | } 221 | } 222 | return $response; 223 | } 224 | 225 | function check_meter($broadcaster,$song_id){ 226 | global $conn; 227 | 228 | $response = TRUE; 229 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster LIKE '$broadcaster'"; 230 | $retval0 = mysqli_query( $conn, $sql0 ); 231 | $numrows = mysqli_num_rows($retval0); 232 | 233 | if($numrows == 1){ 234 | $row0 = mysqli_fetch_assoc($retval0); 235 | $meter_max = $row0["meter_max"]; 236 | if(!empty($meter_max)){ 237 | $sql = "SELECT * FROM sm_notedata WHERE song_id = '$song_id' AND stepstype LIKE 'dance%' AND meter <= '$meter_max'"; 238 | $retval = mysqli_query( $conn, $sql); 239 | if(mysqli_num_rows($retval) == 0){ 240 | $response = FALSE; 241 | } 242 | } 243 | } 244 | return $response; 245 | } 246 | 247 | function check_notedata($broadcaster,$song_id,$stepstype,$difficulty,$user){ 248 | global $conn; 249 | $response = TRUE; 250 | 251 | if(!empty($stepstype) && $stepstype != '%' && empty($difficulty)){ 252 | $difficulty = '%'; 253 | }elseif(empty($stepstype) && !empty($difficulty)){ 254 | die("$user didn't specify a stepstype!"); 255 | } 256 | 257 | $sql = "SELECT * FROM sm_notedata WHERE song_id = '$song_id' AND stepstype LIKE '$stepstype' AND difficulty LIKE '$difficulty'"; 258 | $retval = mysqli_query( $conn, $sql); 259 | if(mysqli_num_rows($retval) == 0){ 260 | $response = FALSE; 261 | } 262 | return $response; 263 | } 264 | 265 | function get_duplicate_song_artist($song_id){ 266 | //check if the title/pack of the songid is a duplicate and return the artist 267 | global $conn; 268 | $response = ""; 269 | 270 | $sql = "SELECT * FROM sm_songs 271 | JOIN sm_songs AS t2 ON 272 | sm_songs.strippedtitle = t2.strippedtitle AND sm_songs.strippedsubtitle = t2.strippedsubtitle AND sm_songs.pack = t2.pack 273 | WHERE t2.id = '$song_id' AND sm_songs.installed = 1"; 274 | $retval = mysqli_query( $conn, $sql); 275 | if(mysqli_num_rows($retval) > 1){ 276 | //the song title is a duplicate in this pack, return the artist of the songid 277 | $sql = "SELECT artist FROM sm_songs WHERE id = '$song_id'"; 278 | $retval = mysqli_query( $conn, $sql); 279 | $response = mysqli_fetch_assoc($retval)['artist']; 280 | $response = " by " . $response; 281 | } 282 | return $response; 283 | } 284 | 285 | function parseCommandArgs($argsStr,$user,$broadcaster){ 286 | global $conn; 287 | 288 | //build a blank array 289 | $result = array('song'=>'','stepstype'=>'','difficulty'=>''); 290 | //split string by '#', keeping the delimiter, and trimming 291 | $args = preg_split('/(?=#)/', $argsStr,-1,PREG_SPLIT_NO_EMPTY); 292 | $args = array_map("trim",$args); 293 | //splice "song", keep arguments in the array 294 | for($i=0; $i < count($args); $i++){ 295 | if(substr($args[$i],0,1) === "#"){ 296 | //$args[$i] = trim(str_replace("#","",$args[$i])); 297 | }else{ 298 | $result['song'] = array_splice($args,$i,1); 299 | } 300 | } 301 | if(is_array($result['song'])){ 302 | $result['song'] = implode("",$result['song']); 303 | } 304 | //remove '#' from the resulting array 305 | $args = array_map(function($str) {return trim(str_replace("#","",$str));},$args); 306 | 307 | if(count($args) == 1 && strlen($args[0]) == 3){ 308 | switch (strtoupper($args[0])){ 309 | case "BSP": 310 | $result['stepstype'] = "dance-single"; 311 | $result['difficulty'] = "Easy"; 312 | break; 313 | case "DSP": 314 | case "MSP": 315 | case "SSP": 316 | $result['stepstype'] = "dance-single"; 317 | $result['difficulty'] = "Medium"; 318 | break; 319 | case "ESP": 320 | case "HSP": 321 | $result['stepstype'] = "dance-single"; 322 | $result['difficulty'] = "Hard"; 323 | break; 324 | case "CSP": 325 | $result['stepstype'] = "dance-single"; 326 | $result['difficulty'] = "Challenge"; 327 | break; 328 | case "XSP": 329 | $result['stepstype'] = "dance-single"; 330 | $result['difficulty'] = "Edit"; 331 | break; 332 | case "BDP": 333 | $result['stepstype'] = "dance-double"; 334 | $result['difficulty'] = "Easy"; 335 | break; 336 | case "DDP": 337 | case "MDP": 338 | case "SDP": 339 | $result['stepstype'] = "dance-double"; 340 | $result['difficulty'] = "Medium"; 341 | break; 342 | case "EDP": 343 | case "HDP": 344 | $result['stepstype'] = "dance-double"; 345 | $result['difficulty'] = "Hard"; 346 | break; 347 | case "CDP": 348 | $result['stepstype'] = "dance-double"; 349 | $result['difficulty'] = "Challenge"; 350 | break; 351 | case "XDP": 352 | $result['stepstype'] = "dance-double"; 353 | $result['difficulty'] = "Edit"; 354 | break; 355 | default: 356 | die("$user gave an invalid 3-letter steps-type/difficulty."); 357 | } 358 | }elseif(count($args) >= 1){ 359 | //$args = array_splice($args,1); 360 | foreach ($args as $arg){ 361 | switch (strtolower($arg)){ 362 | case "single": 363 | case "singles": 364 | case "singlets": 365 | $result['stepstype'] = "dance-single"; 366 | break; 367 | case "double": 368 | case "doubles": 369 | case "doublays": 370 | $result['stepstype'] = "dance-double"; 371 | break; 372 | case "beginner": 373 | $result['difficulty'] = "Beginner"; 374 | break; 375 | case "easy": 376 | case "basic": 377 | case "light": 378 | $result['difficulty'] = "Easy"; 379 | break; 380 | case "medium": 381 | case "standard": 382 | $result['difficulty'] = "Medium"; 383 | break; 384 | case "hard": 385 | case "heavy": 386 | case "expert": 387 | $result['difficulty'] = "Hard"; 388 | break; 389 | case "challenge": 390 | case "oni": 391 | $result['difficulty'] = "Challenge"; 392 | break; 393 | case "edit": 394 | $result['difficulty'] = "Edit"; 395 | break; 396 | default: 397 | die("$user gave invalid steps-type or difficulty."); 398 | } 399 | } 400 | } 401 | 402 | //if stepstype is empty, check if sm_broadcast has one set globally 403 | if(empty($result['stepstype'])){ 404 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster = '$broadcaster'"; 405 | $retval0 = mysqli_query( $conn, $sql0 ); 406 | if(mysqli_num_rows($retval0) == 1){ 407 | $row0 = mysqli_fetch_assoc($retval0); 408 | $result['stepstype'] = $row0["stepstype"]; 409 | } 410 | if(!empty($result['difficulty']) && empty($result['stepstype'])){ 411 | //no stepstype in sm_broadcaster and only difficulty specified 412 | die("$user didn't specify a steps-type!"); 413 | } 414 | }elseif(!empty($result['stepstype'])){ 415 | $sql0 = "SELECT * FROM sm_broadcaster WHERE broadcaster = '$broadcaster'"; 416 | $retval0 = mysqli_query( $conn, $sql0 ); 417 | if(mysqli_num_rows($retval0) == 1){ 418 | $row0 = mysqli_fetch_assoc($retval0); 419 | if($row0['stepstype'] != $result['stepstype'] && !empty($row0['stepstype'])){ 420 | die("$user => The broadcaster has limited requests to \"".$row0['stepstype']."\"."); 421 | } 422 | } 423 | } 424 | 425 | return $result; 426 | } 427 | 428 | function display_ModeDiff($commandArgs){ 429 | //for now we are assuming the game is always StepMania and dance-mode 430 | $displayModeDiff = ""; 431 | if(!empty($commandArgs['stepstype'])){ 432 | $stepstype = ucwords(str_ireplace("dance-","",$commandArgs['stepstype'])); 433 | $displayModeDiff = " [".$stepstype; 434 | if(!empty($commandArgs['difficulty'])){ 435 | $displayModeDiff = $displayModeDiff."/".ucwords($commandArgs['difficulty'])."] "; 436 | }else{ 437 | $displayModeDiff = $displayModeDiff."] "; 438 | } 439 | } 440 | return $displayModeDiff; 441 | } 442 | 443 | function wh_log($log_msg){ 444 | $log_filename = __DIR__."/log"; 445 | if (!file_exists($log_filename)) 446 | { 447 | // create directory/folder uploads. 448 | mkdir($log_filename, 0700, true); 449 | } 450 | $log_file_data = $log_filename.'/log_' . date('Y-m-d') . '.log'; 451 | $log_msg = rtrim($log_msg); //remove line endings 452 | // if you don't add `FILE_APPEND`, the file will be erased each time you add a log 453 | file_put_contents($log_file_data, date("Y-m-d H:i:s") . " -- [" . strtoupper(basename(__FILE__)). "] : ". $log_msg . PHP_EOL, FILE_APPEND); 454 | } 455 | 456 | function check_version($versionClient){ 457 | //check the verion of the incoming scripts to the server version 458 | $versionFilename = __DIR__."/VERSION"; 459 | $githubUrl = "https://github.com/MrTwinkles47/Stepmania-Stream-Tools-MrTwinkles/releases/latest"; 460 | 461 | if(file_exists($versionFilename)){ 462 | $versionServer = file_get_contents($versionFilename); 463 | $versionServer = json_decode($versionServer,TRUE); 464 | $versionServer = $versionServer['version']; 465 | 466 | if($versionServer > $versionClient){ 467 | //wh_log("Script out of date. Client: ".$versionClient." | Server: ".$versionServer); 468 | echo("WARNING! Your client scripts are out of date! Download the latest release at " . PHP_EOL); 469 | echo("$githubUrl Exiting... " . PHP_EOL); 470 | die(); 471 | } 472 | }else{ 473 | $versionServer = 0; 474 | die("Version check error!"); 475 | //wh_log("Server version not found or unexpected value. Check VERSION file in server root directory."); 476 | } 477 | return FALSE; 478 | } 479 | 480 | mysqli_close($conn); 481 | ?> -------------------------------------------------------------------------------- /request_list/stats.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 154 | 155 | 156 | "; 162 | } else { 163 | echo ""; 164 | } 165 | 166 | 167 | if(!isset($_GET["data"])){die("No data set");} 168 | 169 | $conn = mysqli_connect(dbhost, dbuser, dbpass, db); 170 | if(! $conn ) {die('Could not connect: ' . mysqli_error($conn));} 171 | $conn->set_charset("utf8mb4"); 172 | 173 | function getLastRequest(){ 174 | global $conn; 175 | 176 | $return = array('timestamp' => '','broadcaster' => '','username' => '','player_guid' => ''); 177 | 178 | $lastRequest = array(); 179 | $sql = "SELECT song_id,request_time,broadcaster,request_type 180 | FROM sm_requests 181 | WHERE state <> 'canceled' AND state <> 'skipped' 182 | ORDER BY request_time DESC 183 | LIMIT 1"; 184 | $retval = mysqli_query( $conn, $sql ); 185 | $lastRequest = mysqli_fetch_assoc($retval); 186 | $return["timestamp"] = $lastRequest["request_time"]; 187 | $return["broadcaster"] = $lastRequest["broadcaster"]; 188 | 189 | //get lastplayed timestamp 190 | $lastPlayed = array(); 191 | $sql = "SELECT username,player_guid,lastplayed,datetime 192 | FROM sm_songsplayed 193 | ORDER BY lastplayed DESC 194 | LIMIT 1 "; 195 | $retval = mysqli_query( $conn, $sql ); 196 | $lastPlayed = mysqli_fetch_assoc($retval); 197 | 198 | $return["username"] = $lastPlayed["username"]; 199 | $return["player_guid"] = $lastPlayed["player_guid"]; 200 | 201 | if($lastPlayed["datetime"] > $lastRequest["request_time"]){ 202 | //songsplayed datetime is later than the last request, use that timestamp 203 | $return["timestamp"] = $lastPlayed["datetime"]; 204 | } 205 | 206 | return $return; 207 | } 208 | 209 | function format_pack($pack){ 210 | $pack = str_ireplace("Dance Dance Revolution","DDR",$pack); 211 | $pack = str_ireplace("DanceDanceRevolution","DDR",$pack); 212 | $pack = str_ireplace("Dancing Stage","DS",$pack); 213 | $pack = str_ireplace("DancingStage","DS",$pack); 214 | $pack = str_ireplace("In The Groove","ITG",$pack); 215 | $pack = str_ireplace("InTheGroove","ITG",$pack); 216 | //$pack = str_ireplace("Ben Speirs","BS",$pack); 217 | //$pack = str_ireplace("JBEAN Exclusives","JBEAN...",$pack); 218 | $pack = preg_replace("/(\(.*\).\(.*\))$/","",$pack,1); 219 | return $pack; 220 | } 221 | 222 | if(isset($_GET["session"]) && !empty($_GET["session"]) && is_numeric($_GET["session"])){ 223 | $StreamSessionLength = mysqli_real_escape_string($conn,$_GET["session"]); 224 | }else{ 225 | $StreamSessionLength = 6; //stream session length in hours (default: 6) 226 | } 227 | 228 | $timestamp = getLastRequest()['timestamp']; 229 | 230 | switch(strtolower($_GET["data"])){ 231 | ////////REQUESTS///////// 232 | case "requests": 233 | $sql = "SELECT COUNT(*) AS requestsToday FROM sm_requests WHERE state <> 'canceled' AND state <> 'skipped' AND request_time > DATE_SUB('$timestamp', INTERVAL $StreamSessionLength HOUR)"; 234 | $retval = mysqli_query( $conn, $sql ); 235 | 236 | $row = mysqli_fetch_assoc($retval); 237 | $requestsToday = $row["requestsToday"]; 238 | 239 | echo "$requestsToday   requests this session$playedToday   songs played this session"; 250 | break; 251 | case "scores": 252 | ////////SCORES///////// 253 | if(isset($scoreType)){ 254 | switch ($scoreType){ 255 | case "ddr": 256 | $tier = "ddr_tier"; 257 | $grade = "ddr_grade"; 258 | $score = "score"; 259 | break; 260 | case "itg": 261 | default: 262 | $tier = "itg_tier"; 263 | $grade = "itg_grade"; 264 | $score = "percentdp"; 265 | } 266 | }else{die("Score type missing from config.php file!");} 267 | 268 | $sql = "SELECT sm_grade_tiers.$grade,FORMAT(MAX(sm_scores.percentdp*100),2) AS percentdp,FORMAT(MAX(score),0) AS score,COUNT(sm_scores.grade) AS gradeCount 269 | FROM sm_scores 270 | LEFT JOIN sm_grade_tiers ON sm_grade_tiers.$tier = sm_scores.grade 271 | WHERE sm_scores.datetime > DATE_SUB('$timestamp', INTERVAL $StreamSessionLength HOUR) AND sm_scores.grade <> 'Failed' AND sm_scores.percentdp > 0 272 | GROUP BY sm_scores.grade 273 | ORDER BY sm_scores.grade ASC"; 274 | $retval = mysqli_query( $conn, $sql ); 275 | 276 | echo ''; 277 | while ($row = mysqli_fetch_assoc($retval)){ 278 | echo ''; 279 | echo ""; 280 | echo ''; 281 | } 282 | echo '
".$row[$grade]."(".$row[$score].")".$row['gradeCount']."
'; 283 | break; 284 | case "endscreenscroll": 285 | ////////EndScreenScroll///////// 286 | if(isset($scoreType)){ 287 | switch ($scoreType){ 288 | case "ddr": 289 | $tier = "ddr_tier"; 290 | $grade = "ddr_grade"; 291 | $score = "score"; 292 | $stageAwards = array( 293 | "FullComboW3" => "Full Combo", 294 | "SingleDigitW3" => "", 295 | "OneW3" => "1 Great", 296 | "FullComboW2" => "Perfect Full Combo", 297 | "SingleDigitW2" => "", 298 | "OneW2" => "1 Perfect", 299 | "FullComboW1" => "Marvelous Full Combo" 300 | ); 301 | break; 302 | case "itg": 303 | default: 304 | $tier = "itg_tier"; 305 | $grade = "itg_grade"; 306 | $score = "percentdp"; 307 | $stageAwards = array( 308 | "FullComboW3" => "Full Combo", 309 | "SingleDigitW3" => "", 310 | "OneW3" => "1 Great", 311 | "FullComboW2" => "Tri-Star", 312 | "SingleDigitW2" => "", 313 | "OneW2" => "1 Excellent", 314 | "FullComboW1" => "Quad-Star" 315 | ); 316 | } 317 | }else{die("Score type missing from config.php file!");} 318 | 319 | $sql = "SELECT 320 | sm_scores.song_id, 321 | sm_requests.requestor as requestor, 322 | sm_requests.request_time, 323 | sm_requests.request_type, 324 | TRIM(CONCAT(sm_songs.title,' ',sm_songs.subtitle)) AS title, 325 | sm_songs.pack, 326 | CONCAT(FORMAT(sm_scores.percentdp * 100, 2),'%') AS percentdp, 327 | FORMAT(sm_scores.score, 0) AS score, 328 | sm_grade_tiers.$grade AS grade, 329 | sm_scores.stage_award AS award, 330 | sm_scores.datetime 331 | FROM sm_requests 332 | INNER JOIN sm_scores 333 | JOIN sm_grade_tiers 334 | ON sm_grade_tiers.$tier = sm_scores.grade 335 | ON sm_requests.song_id = sm_scores.song_id 336 | INNER JOIN sm_songs 337 | ON sm_requests.song_id = sm_songs.id 338 | WHERE sm_scores.datetime BETWEEN sm_requests.request_time AND sm_requests.timestamp 339 | AND sm_requests.request_time >= DATE_SUB('$timestamp', INTERVAL $StreamSessionLength HOUR) 340 | ORDER BY sm_requests.request_time DESC"; 341 | $retval = mysqli_query( $conn, $sql ); 342 | 343 | echo '
'; 344 | while ($row = mysqli_fetch_assoc($retval)){ 345 | //translate SM5 scores and stage award to game-specific names 346 | $award = $row['award']; 347 | if(array_key_exists($award,$stageAwards)){ 348 | $award = $stageAwards[$award]; 349 | } 350 | echo "

Requestor: ".$row['requestor']."
Song: ".$row['title']."
Score: ".$row[$score]." (".$row['grade'].")"; 351 | if(!empty($award)){ 352 | //Don't show the award if there isn't one 353 | echo "
Award: ".$award; 354 | } 355 | echo "

"; 356 | } 357 | echo '
'; 358 | break; 359 | case "recent": 360 | ////////RECENT HIGHSCORES///////// 361 | if(isset($scoreType)){ 362 | switch ($scoreType){ 363 | case "ddr": 364 | $tier = "ddr_tier"; 365 | $grade = "ddr_grade"; 366 | $score = "score"; 367 | $stageAwards = array( 368 | "FullComboW3" => "FC", 369 | "SingleDigitW3" => "", 370 | "OneW3" => "1G", 371 | "FullComboW2" => "PFC", 372 | "SingleDigitW2" => "", 373 | "OneW2" => "1P", 374 | "FullComboW1" => "MFC" 375 | ); 376 | break; 377 | case "itg": 378 | default: 379 | $tier = "itg_tier"; 380 | $grade = "itg_grade"; 381 | $score = "percentdp"; 382 | $stageAwards = array( 383 | "FullComboW3" => "FC", 384 | "SingleDigitW3" => "", 385 | "OneW3" => "1G", 386 | "FullComboW2" => "Tri-Star", 387 | "SingleDigitW2" => "", 388 | "OneW2" => "1EX", 389 | "FullComboW1" => "Quad" 390 | ); 391 | } 392 | }else{die("Score type missing from config.php file!");} 393 | 394 | $sql = "SELECT TRIM(CONCAT(sm_songs.title,' ',sm_songs.subtitle)) AS title,sm_songs.pack AS pack,sm_grade_tiers.$grade,CONCAT(FORMAT(maxpercentdp * 100, 2),'%') AS percentdp,FORMAT(score, 0) AS score,sm_scores.stage_award AS award,sm_scores.stepstype,sm_scores.difficulty,sm_scores.username,datetime 395 | FROM 396 | sm_scores 397 | JOIN sm_songs ON sm_songs.id = sm_scores.song_id 398 | JOIN sm_grade_tiers ON sm_grade_tiers.$tier = sm_scores.grade 399 | JOIN( 400 | SELECT song_id,stepstype,difficulty,username,MAX(percentdp) AS MaxPercentdp 401 | FROM 402 | sm_scores 403 | WHERE 404 | song_id > 0 AND percentdp > 0 AND grade <> 'Failed' 405 | GROUP BY 406 | song_id,stepstype,difficulty,username 407 | ) AS h2 408 | ON sm_scores.song_id = h2.song_id AND sm_scores.stepstype = h2.stepstype AND sm_scores.difficulty = h2.difficulty AND sm_scores.username = h2.username AND sm_scores.percentdp = h2.maxpercentdp 409 | ORDER BY `datetime` DESC 410 | LIMIT 5"; 411 | $retval = mysqli_query( $conn, $sql ); 412 | 413 | echo ''; 414 | while ($row = mysqli_fetch_assoc($retval)){ 415 | //translate SM5 scores and stage award to game-specific names 416 | $award = $row['award']; 417 | if(array_key_exists($award,$stageAwards)){ 418 | $award = $stageAwards[$award]; 419 | } 420 | //build table of recent highscores 421 | echo ''; 422 | echo ''; 423 | echo ''; 424 | } 425 | echo '
'.$row['title'].''.format_pack($row['pack']).''.$row[$grade].'('.$row[$score].')'.$award.'
'; 426 | break; 427 | case "requestors": 428 | ////////REQUESTORS///////// 429 | $broadcaster = getLastRequest()['broadcaster']; 430 | 431 | $sql = "SELECT requestor,COUNT(id) AS count 432 | FROM sm_requests 433 | WHERE state <> 'canceled' AND state <> 'skipped' AND LOWER(requestor) NOT IN(\"{$broadcaster}\") AND request_time > DATE_SUB('$timestamp', INTERVAL $StreamSessionLength HOUR) 434 | GROUP BY requestor 435 | ORDER BY count DESC,requestor DESC 436 | LIMIT 5"; 437 | $retval = mysqli_query( $conn, $sql ); 438 | 439 | echo '

Special thanks to requestors:

'; 440 | while ($row = mysqli_fetch_assoc($retval)){ 441 | echo "".$row['requestor']." (".$row['count'].")
"; 442 | } 443 | echo ""; 444 | break; 445 | case "requesttypes": 446 | ////////Request Types//////// 447 | $sql = "SELECT request_type,COUNT(request_type) as count 448 | FROM sm_requests 449 | WHERE request_type <> 'normal' AND request_time > DATE_SUB('$timestamp', INTERVAL $StreamSessionLength HOUR) 450 | GROUP BY request_type 451 | ORDER BY count DESC,request_type ASC"; 452 | 453 | $retval = mysqli_query( $conn, $sql ); 454 | while ($row = mysqli_fetch_assoc($retval)){ 455 | $request_type = $row['request_type']; 456 | if($request_type != "normal"){ 457 | $request_img = glob("images/".$request_type.".{png,gif}", GLOB_BRACE); 458 | if (!$request_img){ 459 | $request_img = "images/random.png"; 460 | }else{ 461 | $request_img = "images/".urlencode(basename($request_img[0])); 462 | } 463 | $request_type = ""; 464 | } 465 | 466 | echo $request_type."\n"; 467 | echo "".$row['count']."
"; 468 | 469 | } 470 | echo ""; 471 | break; 472 | ////////Request Status///////// 473 | case "requeststatus": 474 | $sql = "SELECT request_toggle FROM sm_broadcaster"; 475 | $retval = mysqli_query( $conn, $sql ); 476 | if(mysqli_num_rows($retval) == 1){ 477 | //only 1 broadcaster 478 | $row = mysqli_fetch_assoc($retval); 479 | $requestStatus = $row["request_toggle"]; 480 | }elseif(mysqli_num_rows($retval) > 1){ 481 | //more than 1 broadcaster 482 | //user needs to specify which broadcaster 483 | if(!isset($_GET["broadcaster"])){die("No broadcaster set! Usage: [URL]/stats.php?data=requeststatus&broadcaster=[BROADCASTER]");} 484 | if(empty($_GET["broadcaster"]) || strlen($_GET["broadcaster"]) < 3){die("Invalid broadcaster set!");} 485 | $broadcaster = mysqli_real_escape_string($conn,$_GET["broadcaster"]); 486 | 487 | $sql = "SELECT request_toggle FROM sm_broadcaster WHERE broadcaster = \"{$broadcaster}\""; 488 | $retval = mysqli_query( $conn, $sql ); 489 | 490 | if(mysqli_num_rows($retval) == 0){die("Broadcaster not found!");} 491 | 492 | $row = mysqli_fetch_assoc($retval); 493 | $requestStatus = $row["request_toggle"]; 494 | }else{ 495 | $requestStatus = "Error"; 496 | } 497 | if(isset($_GET["onlystate"])){ 498 | echo " $requestStatus "; 499 | }else{ 500 | echo "Current Request Status is: $requestStatus "; 501 | } 502 | break; 503 | default: 504 | echo("Error: No data set! Usage: [URL]/stats.php?data=[requests,songs,scores,recent,endscreenscroll,requestors,requeststatus,requesttypes]"); 505 | break; 506 | } 507 | 508 | echo "\n"; 509 | //close everything out 510 | mysqli_close($conn); 511 | die(); 512 | ?> 513 | --------------------------------------------------------------------------------