Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Commit

Permalink
add channel points!
Browse files Browse the repository at this point in the history
  • Loading branch information
mmattbtw committed Aug 18, 2022
1 parent 873c7fb commit 018e3fe
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 79 deletions.
54 changes: 31 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,63 @@
## `🎶` TwitchTunes [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mmattDonk/TwitchTunes/master.svg)](https://results.pre-commit.ci/latest/github/mmattDonk/TwitchTunes/master)
#### A Python Twitch bot that lets viewers add Spotify songs to your Spotify queue.

#### A Python Twitch bot that lets viewers add Spotify songs to your Spotify queue.

## `💻` Prerequisites

1. Python
- [Windows](https://www.python.org/downloads/)
* Choco: `choco install python`
- [Mac](https://www.python.org/downloads/)
* Brew: `brew install python3` and `brew install pip3`
- [Ubuntu Guide](https://linuxize.com/post/how-to-install-python-3-9-on-ubuntu-20-04/)
- [Windows](https://www.python.org/downloads/)
- Choco: `choco install python`
- [Mac](https://www.python.org/downloads/)
- Brew: `brew install python3` and `brew install pip3`
- [Ubuntu Guide](https://linuxize.com/post/how-to-install-python-3-9-on-ubuntu-20-04/)
2. A Twitch bot account
3. A Spotify account (Spotify Premium is required, this is an API limitation.)

### `` Useful things to have before running `setup.py`

1. [Bot Token](https://twitchapps.com/tmi/)
2. [Twitch Application](https://dev.twitch.tv/console/apps/create)
* Create a `Chat Bot` application
* the OAuth redirect URL can just be `http://localhost`
- Create a `Chat Bot` application
- the OAuth redirect URLs NEED to be `http://localhost:17563/` and `http://localhost:17563`
3. [Spotify Application](https://developer.spotify.com/dashboard/applications)
* Set the Website and Redirect URLs to `http://localhost:8080`
- Set the Website and Redirect URLs to `http://localhost:8080`

### `` Setup

1. Run the `setup.py` script. **<!! RUN `setup.py` ONCE, IF YOU NEED ADDITIONAL FILES, CHECK THE [WIKI](https://github.com/mmattDonk/TwitchTunes/wiki) !!>**
2. Follow all the instructions!

### `` How to request songs

1. !sr {song name}
* You can also use a Spotify URL or URI
* You can get a URI by searching a song on Spotify, then holding down the `Ctrl` key while right clicking on the song, and then selecting "Copy Spotify URI" from the Share menu.
- You can also use a Spotify URL or URI
- You can get a URI by searching a song on Spotify, then holding down the `Ctrl` key while right clicking on the song, and then selecting "Copy Spotify URI" from the Share menu.
2. If your streamer sets up channel points:
- Redeem the TwitchTunes channel point reward!
- In the that shows up, just send a spotify link!

### `💎` Other Commands:
- Variables:

* `{user}` - Twitch Username
- Variables:

- `{user}` - Twitch Username

* `{song}` - Can be a song name, a Spotify URI, or a Spotify URL (for the blacklist commands, it can only be a URI or URL)
- `{song}` - Can be a song name, a Spotify URI, or a Spotify URL (for the blacklist commands, it can only be a URI or URL)

- Commands:
- Commands:

* `!ping` - Checks if the bot is online + shows what version
- `!ping` - Checks if the bot is online + shows what version

* `!blacklistuser {user}` - Blacklists a user from using the bot
- `!blacklistuser {user}` - Blacklists a user from using the bot

* `!unblacklistuser {user}` - Unblacklists a user from using the bot
- `!unblacklistuser {user}` - Unblacklists a user from using the bot

* `!blacklist {song}` - Blacklists a song from being played
- `!blacklist {song}` - Blacklists a song from being played

* `!unblacklist {song}` - Unblacklists a song from being played
- `!unblacklist {song}` - Unblacklists a song from being played

* `!np` - Shows the current song
- `!np` - Shows the current song

* `!songrequest {song}` - Requests a song to be played
- `!songrequest {song}` - Requests a song to be played

* `!lastsong` - Shows the last 10 songs that were played! (we still can't get the Spotify queue from the API https://community.spotify.com/t5/Closed-Ideas/Developer-Web-API-playback-queue-route/idc-p/5387376#M261127 i hate spotify web api...)
- `!lastsong` - Shows the last 10 songs that were played! (we still can't get the Spotify queue from the API https://community.spotify.com/t5/Closed-Ideas/Developer-Web-API-playback-queue-route/idc-p/5387376#M261127 i hate spotify web api...)
149 changes: 113 additions & 36 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import logging
import os
import sys
from typing import Optional

from rich.logging import RichHandler
from twitchio.ext.commands.errors import MissingRequiredArgument
from twitchAPI.oauth import UserAuthenticator
from twitchAPI.pubsub import PubSub
from twitchAPI.twitch import Twitch
from twitchAPI.types import AuthScope

log_level = logging.DEBUG if "dev".lower() in sys.argv else logging.INFO

Expand All @@ -27,15 +29,15 @@ def path_exists(filename):

if not os.path.exists(path_exists("config.json")):
print("\n--------------------------")
print("\nSixth, what's your Twitch Bot's name?")
print("\nwhat's your Twitch Bot's name?")
bot_name = input(
"Type your Bot's name, then press `ENTER`. (You can change this later) "
)
print("\n--------------------------")
print("\nSeventh, what will your prefix be? (example: !, ?, $)")
print("\nwhat will your prefix be? (example: !, ?, $)")
prefix = input("Type your prefix, then press `ENTER`. (You can change this later) ")
print("\n--------------------------")
print("\nEighth, what's your Twitch Channel's name?")
print("\nwhat's your Twitch Channel's name?")
channel = input(
"Type your Channel's name, then press `ENTER`. (You can change this later) "
)
Expand All @@ -53,27 +55,36 @@ def path_exists(filename):
print("\n" * 100)
print("Cool, now let's get to the boring stuff...")
print("\n--------------------------")
print("\nFirst, let's set up the bot's token")
print("\nlet's set up the bot's token")
token = input(
"You can get this token from a site like https://twitchapps.com/tmi/.\nJust copy and paste the OAuth token into here.\nType token, then press `ENTER`. (You can change this later) "
)
print("\n--------------------------")
print("\nSecond, let's setup the Twitch Client ID")
print("\nlet's setup the Twitch Client ID")
client_id = input(
"You can get this by going to https://dev.twitch.tv/console/apps/create, signing in, creating a 'Chat Bot' application (the OAuth redirect URLs can just be 'http://localhost')\nNow just copy and paste the Client ID into here.\nType the Client ID, then press `ENTER`. (You can change this later) "
"You can get this by going to https://dev.twitch.tv/console/apps/create, signing in, creating a 'Chat Bot' application (the OAuth redirect URLs NEED to be 'http://localhost:17563/' and 'http://localhost:17563')\nNow just copy and paste the Client ID into here.\nType the Client ID, then press `ENTER`. (You can change this later) "
)
print("\n--------------------------")
print("\nThird, lets setup the Spotify Client ID")
print("\nlet's setup the Twitch Client Secret")
client_secret = input(
'You can get this by scrolling down on your application, and clicking the "New Secret" button.\nNow just copy and paste the Client Secret into here.\nType the Client Secret, then press `ENTER`. (You can change this later) '
)
print("\n--------------------------")
channel_points_reward = input(
"If you are going to use TwitchTunes with channel points, then what is your Channel Point reward name?\nType the Channel Point reward name, then press `ENTER`. (You can change this later) "
)
print("\n--------------------------")
print("\nlets setup the Spotify Client ID")
spotify_client_id = input(
"You can get this by going to https://developer.spotify.com/dashboard/applications, signing in, then creating an application.\nJust paste in the Client ID into here now.\nType Spotify's Client ID, then press `ENTER`. (You can change this later)"
)
print("\n--------------------------")
print("\nFourth, let's setup the Spotify Client Secret")
print("\nlet's setup the Spotify Client Secret")
spotify_secret = input(
"You can get this by going to that application page, then clicking the 'SHOW CLIENT SECRET' button.\nNow just paste the Client Secret here.\nType Spotify Client Secret, then press `ENTER`. (You can change this later) "
)
print("\n--------------------------")
print("\nFifth, let's setup the Spotify Website/Redirect URI")
print("\nlet's setup the Spotify Website/Redirect URI")
input(
"All you have to do, is hit the settings button, then in BOTH the Website field, AND the Redirect URIs field, but 'http://localhost:8080'\nPress `ENTER` once you have completed this step."
)
Expand All @@ -84,6 +95,10 @@ def path_exists(filename):
+ "# Twitch IRC token\n"
+ f"client_id={client_id}\n"
+ "# Twitch Client ID from dev.twitch.tv\n"
+ f"client_secret={client_secret}\n"
+ "# Twitch Client Secret from dev.twitch.tv\n"
+ f"channel_points_reward={channel_points_reward}\n"
+ "# Channel Point reward name\n"
+ f"\nspotify_client_id={spotify_client_id}\n"
+ "# Get this from the Spotify console https://developer.spotify.com/dashboard/applications\n"
+ f"spotify_secret={spotify_secret}\n"
Expand All @@ -106,8 +121,7 @@ def path_exists(filename):
from pathlib import Path

import dotenv
from aiohttp import request
from twitchio.ext import commands, pubsub
from twitchio.ext import commands

cwd = Path(__file__).parents[0]
cwd = str(cwd)
Expand Down Expand Up @@ -140,6 +154,17 @@ def path_exists(filename):
)


def read_json(filename):
with open(f"{filename}.json", "r") as file:
data = json.load(file)
return data


def write_json(data, filename):
with open(f"{filename}.json", "w") as file:
json.dump(data, file, indent=4)


class Bot(commands.Bot):
def __init__(self):
super().__init__(
Expand All @@ -151,21 +176,12 @@ def __init__(self):
)

self.token = os.environ.get("SPOTIFY_AUTH")
self.version = "1.3.0"
self.version = "1.4.0"

async def event_ready(self):
log.info("\n" * 100)
log.info(f"TwitchTunes ({self.version}) Ready, logged in as: {self.nick}")

def read_json(self, filename):
with open(f"{filename}.json", "r") as file:
data = json.load(file)
return data

def write_json(self, data, filename):
with open(f"{filename}.json", "w") as file:
json.dump(data, file, indent=4)

def is_owner(self, ctx):
return ctx.author.id == "640348450"

Expand Down Expand Up @@ -193,10 +209,10 @@ async def ping_command(self, ctx):
async def blacklist_user(self, ctx, *, user: str):
user = user.lower()
if ctx.author.is_mod or self.is_owner(ctx):
file = self.read_json("blacklist_user")
file = read_json("blacklist_user")
if user not in file["users"]:
file["users"].append(user)
self.write_json(file, "blacklist_user")
write_json(file, "blacklist_user")
await ctx.send(f"{user} added to blacklist")
else:
await ctx.send(f"{user} is already blacklisted")
Expand All @@ -207,10 +223,10 @@ async def blacklist_user(self, ctx, *, user: str):
async def unblacklist_user(self, ctx, *, user: str):
user = user.lower()
if ctx.author.is_mod or self.is_owner(ctx):
file = self.read_json("blacklist_user")
file = read_json("blacklist_user")
if user in file["users"]:
file["users"].remove(user)
self.write_json(file, "blacklist_user")
write_json(file, "blacklist_user")
await ctx.send(f"{user} removed from blacklist")
else:
await ctx.send(f"{user} is not blacklisted")
Expand All @@ -220,7 +236,7 @@ async def unblacklist_user(self, ctx, *, user: str):
@commands.command(name="blacklist", aliases=["blacklistsong", "blacklistadd"])
async def blacklist_command(self, ctx, *, song_uri: str):
if ctx.author.is_mod or self.is_owner(ctx):
jscon = self.read_json("blacklist")
jscon = read_json("blacklist")

song_uri = song_uri.replace("spotify:track:", "")

Expand All @@ -236,7 +252,7 @@ async def blacklist_command(self, ctx, *, song_uri: str):

jscon["blacklist"].append(song_uri)

self.write_json(jscon, "blacklist")
write_json(jscon, "blacklist")

await ctx.send(f"Added {track_name} to blacklist.")

Expand All @@ -251,7 +267,7 @@ async def blacklist_command(self, ctx, *, song_uri: str):
)
async def unblacklist_command(self, ctx, *, song_uri: str):
if ctx.author.is_mod or self.is_owner(ctx):
jscon = self.read_json("blacklist")
jscon = read_json("blacklist")

song_uri = song_uri.replace("spotify:track:", "")

Expand All @@ -262,7 +278,7 @@ async def unblacklist_command(self, ctx, *, song_uri: str):

if song_uri in jscon["blacklist"]:
jscon["blacklist"].remove(song_uri)
self.write_json(jscon, "blacklist")
write_json(jscon, "blacklist")
await ctx.send("Removed that song from the blacklist.")

else:
Expand Down Expand Up @@ -318,10 +334,10 @@ async def songrequest_command(self, ctx, *, song: str):
and re.match(URL_REGEX, song)
):
song_uri = song
await self.song_request(ctx, song_uri, song_uri, album=False)
await self.chat_song_request(ctx, song_uri, song_uri, album=False)

else:
await self.song_request(ctx, song, song_uri, album=False)
await self.chat_song_request(ctx, song, song_uri, album=False)

# @commands.command(name="skip")
# async def skip_song_command(self, ctx):
Expand Down Expand Up @@ -363,12 +379,12 @@ async def songrequest_command(self, ctx, *, song: str):
# await ctx.send(f"Album Requested! {data['name']}")
# return

async def song_request(self, ctx, song, song_uri, album: bool):
blacklisted_users = self.read_json("blacklist_user")["users"]
async def chat_song_request(self, ctx, song, song_uri, album: bool):
blacklisted_users = read_json("blacklist_user")["users"]
if ctx.author.name.lower() in blacklisted_users:
await ctx.send("You are blacklisted from requesting songs.")
else:
jscon = self.read_json("blacklist")
jscon = read_json("blacklist")

if song_uri is None:
data = sp.search(song, limit=1, type="track", market="US")
Expand Down Expand Up @@ -401,5 +417,66 @@ async def song_request(self, ctx, song, song_uri, album: bool):
)


def song_request(data, song, song_uri, album: bool):
jscon = read_json("blacklist")
if song_uri is None:
data = sp.search(song, limit=1, type="track", market="US")
song_uri = data["tracks"]["items"][0]["uri"]
elif re.match(URL_REGEX, song_uri):
data = sp.track(song_uri)
song_uri = data["uri"]
song_uri = song_uri.replace("spotify:track:", "")
song_id = song_uri.replace("spotify:track:", "")
if not album:
data = sp.track(song_id)
duration = data["duration_ms"] / 60000
if song_uri != "not found":
if song_id in jscon["blacklist"] or duration > 17:
return
else:
sp.add_to_queue(song_uri)


def callback_channel_points(uuid, data: dict) -> None:
if (
data["data"]["redemption"]["reward"]["title"].lower()
!= os.environ.get("channel_points_reward").lower()
):
return

log.debug(data)

song: str = data["data"]["redemption"]["user_input"]
ctx = None
blacklisted_users = read_json("blacklist_user")["users"]
if data["data"]["redemption"]["user"]["login"] in blacklisted_users:
return
if (
song.startswith("spotify:track:")
or not song.startswith("spotify:track:")
and re.match(URL_REGEX, song)
):
song_uri = song
song_request(ctx, song_uri, song_uri, album=False)
else:
song_request(ctx, song, song_uri, album=False)


if os.environ.get("channel_points_reward"):
channel_points_reward = os.environ.get("channel_points_reward")
twitch = Twitch(os.environ.get("client_id"), os.environ.get("client_secret"))
twitch.authenticate_app([])
target_scope: list = [AuthScope.CHANNEL_READ_REDEMPTIONS]
auth = UserAuthenticator(twitch, target_scope, force_verify=False)
token, refresh_token = auth.authenticate()
# add User authentication
twitch.set_user_authentication(token, target_scope, refresh_token)

user_id: str = twitch.get_users(logins=config["channels"])["data"][0]["id"]

pubsub = PubSub(twitch)
uuid = pubsub.listen_channel_points(user_id, callback_channel_points)
pubsub.start()

bot = Bot()
bot.run()
Loading

0 comments on commit 018e3fe

Please sign in to comment.