├── img ├── config.png ├── running.png ├── sidebar-big.png ├── account-created.png ├── create-account.png └── sidebar-small.png ├── .gitignore ├── LICENSE ├── cpanfile ├── config.ini.example ├── discordnp.pl ├── Makefile ├── lib └── Discord │ └── NP.pm └── README.md /img/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsTerminus/Discord-NP/HEAD/img/config.png -------------------------------------------------------------------------------- /img/running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsTerminus/Discord-NP/HEAD/img/running.png -------------------------------------------------------------------------------- /img/sidebar-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsTerminus/Discord-NP/HEAD/img/sidebar-big.png -------------------------------------------------------------------------------- /img/account-created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsTerminus/Discord-NP/HEAD/img/account-created.png -------------------------------------------------------------------------------- /img/create-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsTerminus/Discord-NP/HEAD/img/create-account.png -------------------------------------------------------------------------------- /img/sidebar-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsTerminus/Discord-NP/HEAD/img/sidebar-small.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the compiled package(s) 2 | build/discordnp-* 3 | 4 | # Ignore the build dependency cache 5 | build/depcache 6 | 7 | # Ignore your personal config file 8 | config.ini 9 | 10 | # Ignore all log files 11 | *.log 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 vsTerminus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | # To use: cpanm --installdeps . 2 | 3 | # Mojo::Discord and Mojo::WebService::LastFM are not up on cpan yet. 4 | # You'll have to get it from my github and install them yourself into your perl lib folder. 5 | 6 | requires 'Moo'; # OO Framework 7 | requires 'strictures', '>=2, <3'; # Enables strict and warnings with specific settings 8 | requires 'Mojo::IOLoop'; # Simple event loop for persistent websocket connection 9 | requires 'Mojo::IOLoop::TLS'; # PAR doesn't pick this up, but it's needed 10 | requires 'IO::Socket::SSL', '2.009'; # Required for TLS support in Mojo 11 | requires 'Config::Tiny'; # For reading config.ini 12 | requires 'Getopt::Long'; # For passing parameters 13 | requires 'Data::Dumper'; # For debugging complex objects 14 | requires 'namespace::clean'; # Removes declared and imported symbols from your compiled package 15 | requires 'FindBin' => '1.51'; # Used to include libs in cwd 16 | requires 'File::Spec'; # Used for finding included files when packaged with PAR 17 | requires 'File::Basename'; # For printing information without the entire absolute path 18 | requires 'Mojo::WebService::LastFM'; # Now that this is in CPAN we can finally include it here. 19 | 20 | build_requires 'pp'; # To run the makefile you need Par::Packer's pp tool 21 | 22 | -------------------------------------------------------------------------------- /config.ini.example: -------------------------------------------------------------------------------- 1 | [lastfm] 2 | # You will need a Last.FM Developer API account for this script to work. 3 | # There is no way around it, since the request requires an API key and I will not share my own. 4 | # Fortunately, this is easy to get! Simply head over to http://www.last.fm/api/account/create 5 | # Once your account is created, the site will display your API Key and API Secret. 6 | # WRITE THESE DOWN! Last.FM apparently has no way to look them up later, so don't lose them. 7 | # This script only requires the api key. 8 | api_key = xxx 9 | 10 | # You'll also need to provide your Last.FM username for the script to query. 11 | username = xxx 12 | 13 | # Provide an interval (in seconds). This tells the script how often it should 14 | # check what you're listening to. 15 | interval = 60 16 | 17 | # These next two determine what shows up in the sidebar under your name where it says "Listening to ..." 18 | # Yes to both: "artist - title" (eg "Devin Townsend - Genesis") 19 | # Yes to artist only: "artist" (eg "Devin Townsend") 20 | # Yes to title only: "title" (eg "Genesis") 21 | # No to both: "Music" 22 | # In any case, clicking on your name will show Artist, Title, and Album info in the popup. 23 | show_artist = yes 24 | show_title = yes 25 | 26 | [discord] 27 | # You need your discord user token so the script can log in as you and set your status. 28 | # 29 | # DISCLAIMER: 30 | # YOU SHOULD CONSIDER THIS CAREFULLY. The discord token is like your password. Take care not to share it with anyone! 31 | # You should absolutely look at the source code for yourself before you entrust any application (even this one!) with your token. 32 | # 33 | # If you still wish to proceed, learn how to get your token from here: https://discordhelp.net/discord-token 34 | # It should look something like this (but longer): Mzk1MjY1.DgAknw.IiTvP8eSNRD 35 | token = xxx 36 | 37 | # Logging - This script will record a log of events to disk. 38 | # log_dir should be set to a folder you have access to. Do not add a trailing slash. 39 | # log_level can be one of: debug, info, warn, error, fatal, none 40 | # Default value logs only warnings and fatal errors, and defaults to the same folder the script is in. 41 | log_dir=. 42 | log_level=fatal 43 | 44 | -------------------------------------------------------------------------------- /discordnp.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use v5.10; 4 | use strict; 5 | use warnings; 6 | 7 | use FindBin 1.51 qw( $RealBin ); 8 | use lib "$RealBin/lib"; 9 | 10 | use File::Spec; 11 | use File::Basename; 12 | 13 | my $print_par = 0; # Set to 1 to print the PAR directory for debugging packed executables 14 | BEGIN { 15 | if( exists $ENV{PAR_TEMP} and defined $ENV{PAR_TEMP} ) 16 | { 17 | # If this is a PAR packed executable, include the packed lib directory so we get packed libraries in our path. 18 | my $par_lib = $ENV{PAR_TEMP} . '/inc/lib'; 19 | push @INC, "$par_lib"; 20 | 21 | if ( $print_par ) 22 | { 23 | say Data::Dumper->Dump([$ENV{PAR_TEMP}], ['par_temp']); 24 | say Data::Dumper->Dump([$par_lib], ['par_lib']); 25 | } 26 | } 27 | } 28 | 29 | use Getopt::Long; 30 | use Config::Tiny; 31 | use Discord::NP; 32 | use Mojo::IOLoop; 33 | 34 | # use IO::Socket::SSL qw(debug99); # Debugging output for troubleshooting connections 35 | 36 | # This file is responsible for reading the config, creating the Discord::NP object, and calling init(). 37 | 38 | ###################### Load and Validate Config ################### 39 | 40 | my $config = Config::Tiny->new; 41 | my $config_file = "$RealBin/config.ini"; 42 | my $kill_timer = 0; 43 | 44 | GetOptions ("config=s" => \$config_file, 45 | "kill_timer:i" => \$kill_timer, 46 | ) or die ("Error in command line args\n"); 47 | 48 | # Make sure config file exists 49 | die("Could not find config file. File does not exist or is not accessible.\n" . 50 | "File specified: " . $config_file . "\n") 51 | unless -f $config_file; 52 | $config = Config::Tiny->read( $config_file, 'utf8' ); 53 | say localtime(time) . " - Loaded Config: " . basename( $config_file ); 54 | 55 | # Make sure the token is defined and looks kind of like a discord token 56 | my $discord_token = $config->{'discord'}{'token'}; 57 | $discord_token =~ s/^(?:token:)?"?"?(.*?)"?"?$/$1/; # Extract the actual token if they user copypasted the entire field from their browser. 58 | die("Could not read Discord Token value.\n" . 59 | "Expected: A string of letters, numbers, periods, hyphens, and underscores. No quotes, colons, spaces, or other characters.\n" . 60 | "Value as entered: \n" . $discord_token) 61 | unless length $discord_token > 0 and $discord_token =~ /^[A-Za-z0-9-_\.]+$/; 62 | say localtime(time) . " - Discord Token: OK"; 63 | 64 | # Make sure the LastFM API Key is defined and looks like a hex string 65 | my $lastfm_key = $config->{'lastfm'}{'api_key'}; 66 | $lastfm_key =~ s/^"?(.*?)"?$/$1/; 67 | die("Could not read LastFM API Key value.\n" . 68 | "Expected: A string of hexadecimal characters 0-9 and a-f. No special characters or spaces.\n" . 69 | "Value as entered: \n" . $lastfm_key) 70 | unless length $lastfm_key > 0 and $lastfm_key =~ /^[A-Fa-f0-9]+$/; 71 | say localtime(time) . " - LastFM API Key: OK"; 72 | 73 | # Make sure the LastFM Username is defined 74 | die("Could not read LastFM Username value.\n") unless exists $config->{'lastfm'}{'username'} and length $config->{'lastfm'}{'username'} > 0; 75 | say localtime(time) . " - LastFM User: " . $config->{'lastfm'}{'username'}; 76 | 77 | my $show_artist = ( lc $config->{'lastfm'}{'show_artist'} eq 'yes' ? 1 : 0 ); 78 | my $show_title = ( lc $config->{'lastfm'}{'show_title'} eq 'yes' ? 1 : 0 ); 79 | 80 | ###################### End Validation ########################### 81 | 82 | my $np = Discord::NP->new( 83 | 'interval' => $config->{'lastfm'}{'interval'}, 84 | 'lastfm_user' => $config->{'lastfm'}{'username'}, 85 | 'lastfm_key' => $lastfm_key, 86 | 'discord_token' => $discord_token, 87 | 'logdir' => $config->{'discord'}{'log_dir'}, 88 | 'loglevel' => $config->{'discord'}{'log_level'}, 89 | 'show_artist' => $show_artist, 90 | 'show_title' => $show_title, 91 | ); 92 | 93 | # Used for PAR::Packer so it can run the application temporarily to gather dependencies. 94 | Mojo::IOLoop->timer($kill_timer => sub { $np->discord->disconnect; Mojo::IOLoop->stop; }) if $kill_timer > 0; 95 | 96 | $np->init(); 97 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Make sure $PERL5LIB is defined and valid. 2 | 3 | # PAR::Packer does not seem to pick up OpenSSL shared libraries on any platform. 4 | # To remedy this we will manually specify the locations of libcrypto and libssl 5 | # Additionally Windows requires zlib 6 | 7 | # Windows - The DLLs we need are in the Strawberry Perl Installation Folder 8 | # Assumes default installation. Change as required. 9 | STRAWBERRY_BIN=C:\Strawberry\c\bin 10 | WIN_ZLIB=$(STRAWBERRY_BIN)\ZLIB1__.DLL 11 | WIN_LIBCRYPTO=$(STRAWBERRY_BIN)\LIBCRYPTO-1_1-X64__.DLL 12 | WIN_LIBSSL=$(STRAWBERRY_BIN)\LIBSSL-1_1-X64__.DLL 13 | 14 | # Shared Object dependencies for perl on Linux 15 | # We can use ldd to identify everything needed by perl and include all of it. 16 | LDD=$(shell ldd `which perl` | grep '=>' | cut -d' ' -f3 | sed 's/^/--link=/' | awk '{print}' ORS=' ') 17 | 18 | # Linux - Should be able to just get them from /usr/lib if they were installed with your package manager 19 | LIN_SO_DIR=/usr/lib 20 | LIN_LIBCRYPTO=$(LIN_SO_DIR)/libcrypto.so.1.1 21 | LIN_LIBSSL=$(LIN_SO_DIR)/libssl.so.1.1 22 | LIN_LIBCRYPT=$(LIN_SO_DIR)/libcrypt.so.2 23 | 24 | # OpenSSL Dynamic Libs for macOS 25 | # My openssl is installed via homebrew. Change as required. 26 | MAC_DY_DIR=/usr/local/Cellar/openssl@1.1/1.1.1i/lib 27 | MAC_LIBCRYPTO=$(MAC_DY_DIR)/libcrypto.1.1.dylib 28 | MAC_LIBSSL=$(MAC_DY_DIR)/libssl.1.1.dylib 29 | 30 | # Point to a valid config file so the script can execute and PAR can collect dependencies. 31 | CONFIG_FILE=config.ini 32 | 33 | # Mojo requires html_entities and Commands.pm 34 | ENTITIES_FILE=$(PERL5LIB)/Mojo/resources/html_entities.txt 35 | COMMANDS_FILE=$(PERL5LIB)/Mojolicious/Commands.pm 36 | 37 | # Also need two folders, the Mojolicious public and templates folders.: 38 | MOJO_PUBLIC=$(PERL5LIB)/Mojolicious/public 39 | MOJO_TEMPLATES=$(PERL5LIB)/Mojolicious/templates 40 | 41 | # Mojo::IOLoop needs its resources folder 42 | IOLOOP_RESOURCES=$(PERL5LIB)/Mojo/IOLoop/resources 43 | 44 | # Need the TLS.pm file - an unfortunate hack so that Cwd::realpath doesn't fail. Temporary? 45 | IOLOOP_TLS_PM=$(PERL5LIB)/Mojo/IOLoop/TLS.pm 46 | 47 | # Now we need the locations of the "lib" folders for Mojo::Discord, Mojo::WebService::LastFM, and Discord::NP, since they won't be in your perl5/lib folder. 48 | DISCORD_NP_LIB=$(PERL5LIB)/Discord/NP/lib 49 | MOJO_WEBSERVICE_LASTFM_LIB=$(PERL5LIB)/Mojo/WebService/LastFM/lib 50 | MOJO_DISCORD_LIB=$(PERL5LIB)/Mojo/Discord/lib 51 | 52 | #CACERT File 53 | CACERT=$(PERL5LIB)/Mozilla/CA/cacert.pem 54 | 55 | # Kill timer tells discordnp.pl to stop executing after the specified number of seconds. We can use this for pp to gather dependencies. 56 | KILL_TIMER=5 57 | 58 | define PP_ARGS 59 | --execute \ 60 | --xargs="--config=$(CONFIG_FILE) --kill_timer=$(KILL_TIMER)" \ 61 | --cachedeps=build/depcache \ 62 | --addfile="$(ENTITIES_FILE);lib/Mojo/resources/html_entities.txt" \ 63 | --addfile="$(COMMANDS_FILE);lib/Mojolicious/Commands.pm" \ 64 | --addfile="$(IOLOOP_RESOURCES);lib/Mojo/IOLoop/resources" \ 65 | --addfile="$(CACERT);cacert.pem" \ 66 | --lib=$(MOJO_DISCORD_LIB) \ 67 | --lib=$(MOJO_WEBSERVICE_LASTFM_LIB) \ 68 | --lib=$(DISCORD_NP_LIB) \ 69 | --module="Mojo::IOLoop::TLS" \ 70 | --module="IO::Socket::SSL" \ 71 | --module="Net::SSLeay" \ 72 | --unicode 73 | endef 74 | 75 | # File extension (Only used for Windows 76 | EXT= 77 | ifeq ($(OS),Windows_NT) 78 | OSTYPE=windows 79 | EXT=.exe 80 | PP_ARGS+=--link=$(WIN_ZLIB) 81 | PP_ARGS+=--link=$(WIN_LIBCRYPTO) 82 | PP_ARGS+=--link=$(WIN_LIBSSL) 83 | else 84 | UNAME_S:=$(shell uname -s) 85 | ifeq ($(UNAME_S),Linux) 86 | OSTYPE=linux 87 | PP_ARGS+=--link=$(LIN_LIBCRYPTO) 88 | PP_ARGS+=--link=$(LIN_LIBSSL) 89 | PP_ARGS+=--link=$(LIN_LIBCRYPT) 90 | PP_ARGS+=$(LDD) 91 | endif 92 | ifeq ($(UNAME_S),Darwin) 93 | OSTYPE=macos 94 | PP_ARGS+=--link=$(MAC_LIBCRYPTO) 95 | PP_ARGS+=--link=$(MAC_LIBSSL) 96 | endif 97 | endif 98 | 99 | PP_ARGS+=--output="build/discordnp-$(OSTYPE)$(EXT)" 100 | 101 | default: 102 | @echo "Building Discord-NP for $(OSTYPE)" 103 | pp $(PP_ARGS) discordnp.pl 104 | @echo "Wrote file: build/discordnp-$(OSTYPE)$(EXT)" 105 | 106 | clean: 107 | rm build/depcache 108 | rm build/discordnp-* 109 | rm build/mojo-discord.log 110 | rm mojo-discord.log 111 | 112 | cleanwin: 113 | del build\depcache 114 | del build\discordnp-windows.exe 115 | del build\mojo-discord.log 116 | del mojo-discord.log 117 | -------------------------------------------------------------------------------- /lib/Discord/NP.pm: -------------------------------------------------------------------------------- 1 | package Discord::NP; 2 | use feature 'say'; 3 | binmode(STDOUT, ":utf8"); 4 | 5 | use Moo; 6 | use strictures 2; 7 | use Mojo::WebService::LastFM; 8 | use Mojo::Discord; 9 | use Mojo::IOLoop; 10 | use Data::Dumper; 11 | use namespace::clean; 12 | 13 | # Read the config file 14 | # Default to "config.ini" unless one is passed in as an argument 15 | has 'interval' => ( is => 'rw', default => 60 ); 16 | has 'discord_token' => ( is => 'ro' ); 17 | has 'discord' => ( is => 'lazy', builder => sub { 18 | my $self = shift; 19 | my $discord = Mojo::Discord->new( 20 | 'token' => $self->discord_token, 21 | 'token_type' => 'Bearer', 22 | 'name' => 'Discord Now Playing', 23 | 'url' => 'https://github.com/vsterminus', 24 | 'version' => '1.0', 25 | 'logdir' => $self->logdir, 26 | 'loglevel' => $self->loglevel, 27 | 'reconnect' => 1, 28 | ); 29 | }); 30 | has 'logdir' => ( is => 'ro' ); 31 | has 'loglevel' => ( is => 'ro' ); 32 | has 'lastfm_user' => ( is => 'ro' ); 33 | has 'lastfm_key' => ( is => 'ro' ); 34 | has 'lastfm' => ( is => 'lazy', builder => sub { Mojo::WebService::LastFM->new( api_key => shift->lastfm_key ) } ); 35 | has 'my_id' => ( is => 'rw' ); 36 | has 'last_status' => ( is => 'rw', default => '' ); 37 | has 'show_artist' => ( is => 'ro' ); 38 | has 'show_title' => ( is => 'ro' ); 39 | 40 | # Poll Last.FM and update the user's Discord status 41 | sub update_status 42 | { 43 | my ($self, $update) = @_; 44 | 45 | # Mojo::WebService::LastFM lets us optionally specify a format to return the results in. 46 | # Without it we would just get a hashref back containing all of the values. 47 | # For this script all we need is Artist - Title. 48 | # 49 | # This is a non-blocking call using Mojo::Promise. 50 | $self->lastfm->nowplaying_p({ 'username' => $self->lastfm_user })->then(sub 51 | { 52 | my $json = shift; 53 | unless ( defined $json and exists $json->{'artist'} and $json->{'title'} ) 54 | { 55 | say "LastFM lookup failed!"; 56 | #say Dumper($json); 57 | return undef; 58 | } 59 | my $nowplaying = $json->{'artist'} . ' - ' . $json->{'title'}; 60 | 61 | my $sidebar = "Music"; 62 | if ( $self->show_artist and $self->show_title ) { $sidebar = $nowplaying } 63 | elsif ( $self->show_artist ) { $sidebar = $json->{'artist'} } 64 | elsif ( $self->show_title ) { $sidebar = $json->{'title'} } 65 | 66 | # Only update if the song is currently playing 67 | # We can identify that by $lastfm->{'date'} being undefined. 68 | if ( defined $nowplaying and !defined $json->{'date'} ) 69 | { 70 | # Now connect to discord. Receiving the READY packet from Discord will trigger the status update automatically. 71 | $self->discord->status_update({ 72 | 'name' => $sidebar, 73 | 'type' => 2, # Listening to... $np 74 | 'details' => $nowplaying, 75 | 'state' => $json->{'album'} 76 | }); 77 | 78 | say localtime(time) . " - Now Playing: $nowplaying" if $nowplaying ne $self->last_status; 79 | $self->last_status($nowplaying); 80 | } 81 | else 82 | { 83 | # Set 'Nothing' only once and remove the status message entirely 84 | if ( $self->last_status ne 'Nothing' ) 85 | { 86 | $self->discord->status_update({ 87 | 'name' => undef, 88 | 'type' => 0 89 | }); 90 | 91 | say localtime(time) . " - Nothing is currently playing."; 92 | $self->last_status("Nothing"); 93 | } 94 | } 95 | })->catch(sub 96 | { 97 | say "Last.FM lookup failed!"; 98 | say Dumper(shift); 99 | }); 100 | } 101 | 102 | sub add_me 103 | { 104 | my ($self, $user) = @_; 105 | $self->my_id($user->{'id'}); 106 | } 107 | 108 | sub init 109 | { 110 | my $self = shift; 111 | 112 | $self->discord->gw->on('READY' => sub 113 | { 114 | my ($gw, $hash) = @_; 115 | say localtime(time) . " - Connected to Discord"; 116 | $self->add_me($hash->{'user'}); 117 | $self->update_status(); 118 | }); 119 | 120 | $self->discord->gw->on('FINISH', => sub { 121 | say localtime(time) . " - Disconnected from Discord"; 122 | }); 123 | 124 | # Connect to Discord 125 | $self->discord->init(); 126 | 127 | # Update status on a recurring timer 128 | Mojo::IOLoop->recurring($self->interval => sub { $self->update_status(); }); 129 | 130 | # Start IOLoop - Nothing below this line will execute until the loop ends (never). 131 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running; 132 | } 133 | 134 | 1; 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord Now Playing 2 | 3 | # Please Note: this project no longer works. 4 | 5 | Discord made changes to the way statuses are set on users. This script will still connect and claim to be working but nothing will appear on your profile in Discord. 6 | 7 | I don't have the time nor inclination right now to figure out how to fix it. For the foreseeable future you will need to find an alternative to this project. 8 | 9 | ------- 10 | 11 | ![GitHub](https://img.shields.io/github/license/vsTerminus/Discord-NP) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/vsTerminus/Discord-NP) 12 | 13 | This script will update your Discord "Listening to" status with whatever you are listening to according to Last.FM 14 | 15 | ![Discord Screenshot](/img/sidebar-big.png) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/vsTerminus/Discord-NP) 16 | 17 | Setup will take a few minutes and requires two main things: 18 | 19 | ## **1. Last.FM API Key** 20 | 21 | Head over to the [Last.FM API Page](https://www.last.fm/api/account/create) and sign in with your existing Last.FM username and password. It should bring you to the **Create API account** page and ask you for a few things. 22 | 23 | It doesn't really matter what you put in most of the fields, but it should probably look something like this: 24 | 25 | ![LastFM Create API Account Screenshot](/img/create-account.png) 26 | 27 | After clicking Submit you should get a confirmation page with two items: *API Key* and *Shared Secret*. The API Key is the only one you need for this, but I recommend you save both for future use just in case, as they don't actually provide a way to retrieve these later. 28 | 29 | ![LastFM API Account Created Screenshot](/img/account-created.png) 30 | 31 | Copy and paste the API Key value into the config file in the `api_key = xxx` line 32 | 33 | ## **2. Discord User Token** 34 | 35 | For this one you'll need to use the Desktop or Web app - it will not work on mobile. 36 | 37 | WARNING: Anyone who knows your Discord token has **FULL ACCESS** to your account. Do not share it with anyone! 38 | You *should* read this application's source code for yourself and make sure you trust it before inputting your token. 39 | 40 | If you still want to proceed, head over to https://discordhelp.net/discord-token and follow the instructions there. 41 | 42 | ## When you're done 43 | 44 | Save your config file as "*config.ini*" and it should look something like this: 45 | 46 | ![Finished Config File](/img/config.png) 47 | 48 | Now just run the executable. It should connect to Discord and immediately start setting your "*Playing*" status to whatever you're listening to on Last.FM 49 | 50 | If it's working, it will look something like this: 51 | 52 | ![Running Executable](/img/running.png) 53 | 54 | 55 | 56 | ## Run from Source 57 | 58 | If you want to contribute to the code or just prefer to run the raw perl script instead of my packaged executable, you can do that too. 59 | 60 | ### Linux, MacOS 61 | 62 | #### Install and Configure Perl + cpanminus 63 | 64 | **Install Perl** 65 | You should already have Perl. If you don't, you need at least v5.10 -- Install it with your package manager. 66 | On MacOS I recommend installing perl from Homebrew instead of system perl. It's probably not necessary for this particular project, but I recommend it in general. 67 | 68 | **Install cpanminus** 69 | cpanminus is an excellent tool for managing CPAN modules. Simpler and more powerful than the included CPAN shell. You may be able to install this through your package manager or you can install it with the `cpan install cpanminus` command. If you go the second route you'll have to go through the CPAN setup, which just asks you a number of questions you can (probably) just hit "Enter" and accept the defaults for every single one. 70 | 71 | **Configure cpanminus** 72 | Perl can install modules to the system for all users (which requires sudo) or it can install to your home directory (a "local" lib(rary)) for your user alone. This doesn't require elevated permissions and is generally preferred. But you do need to set up a couple things: 73 | 74 | 1. Set up your local lib 75 | 76 | You should add the following to either `~/.bash_profile` or `~/.zprofile` (or if you are using a shell other than bash or zsh, in that shell's profile file). Remember to replace "username" with your own username. Use your favorite text editor. 77 | 78 | ```bash 79 | export PERL_MB_OPT='--install_base /home/username/perl5' 80 | export PERL_MM_OPT='INSTALL_BASE=/home/username/perl5' 81 | export PERL5LIB="/home/username/perl5/lib/perl5" 82 | export PATH="/home/username/perl5/bin:$PATH" 83 | export PERL_LOCAL_LIB_ROOT="/home/usename/perl5:$PERL_LOCAL_LIB_ROOT" 84 | ``` 85 | 86 | Now either restart your terminal or re-source your profile (eg `source ~/.zprofile`) 87 | 88 | 2. Configure cpanminus 89 | 90 | You should be able to just run 91 | 92 | ```bash 93 | cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) 94 | ``` 95 | 96 | If you try to install something with cpanm it will complain and prompt you to do this anyway. 97 | 98 | Now anything you install with cpanminus will go to the local lib in ~/perl5 instead of to a system directory. 99 | 100 | **Install Discord::NP** 101 | 102 | ```bash 103 | # Clone the repository 104 | git clone https://github.com/vsTerminus/Discord-NP.git 105 | 106 | # Enter the project directory 107 | cd Discord-NP 108 | ``` 109 | 110 | In the root directory of this project you should see a file called "cpanfile" which contains a list of this project's dependencies. You can point cpanminus at it to install them: 111 | 112 | ```bash 113 | cpanm --installdeps . 114 | ``` 115 | 116 | This will install everything except for one library: 117 | 118 | **Install Mojo::Discord** 119 | 120 | [Mojo::Discord](https://github.com/vsTerminus/Mojo-Discord) is the library required for this application to connect to Discord in the first place. It will go up on CPAN eventually and then you'll be able to install it with cpanm, but it needs more unit tests and documentation first. So for now you'll have to install it manually. 121 | 122 | Luckily it's not too difficult: 123 | 124 | ```bash 125 | # Check out the repository 126 | git clone https://github.com/vsTerminus/Mojo-Discord.git 127 | 128 | # Enter the project directory 129 | cd Mojo-Discord 130 | 131 | # Install Mojo-Discord's dependencies 132 | cpanm --installdeps . 133 | 134 | # Manually install Mojo::Discord by creating symlinks inside your local lib 135 | # This way you can update it just by running "git pull" in the future. 136 | ln -s $PWD/Mojo-Discord/lib/Mojo/Discord.pm $PERL5LIB/Mojo/ 137 | ln -s $PWD/Mojo-Discord/lib/Mojo/Discord $PERL5LIB/Mojo/ 138 | ``` 139 | 140 | To validate that both modules are installed, 141 | 142 | ```bash 143 | perl -MMojo::Discord -MMojo::WebService::LastFM -e 1 144 | ``` 145 | 146 | If you don't see any errors then you got it right. Congrats! 147 | 148 | **Configure the App** 149 | 150 | Create a copy of the example config file named "config.ini" 151 | 152 | ```bash 153 | cp config.ini.example config.ini 154 | ``` 155 | 156 | Use your favorite text editor to fill it out as you saw in the first section of this readme. 157 | 158 | **Run the App** 159 | 160 | Should be as simple as 161 | 162 | ```bash 163 | perl discordnp.pl 164 | ``` 165 | 166 | from inside the Discord::NP project folder. 167 | 168 | 169 | ### Windows 170 | 171 | I recommend installing [Strawberry Perl](http://strawberryperl.com/), as it comes with cpanminus already installed. 172 | 173 | You need to check out two repositories 174 | 175 | - [Discord::NP](https://github.com/vsTerminus/Discord-NP) (This one!) 176 | - [Mojo::Discord](https://github.com/vsTerminus/Mojo-Discord) (For connecting to Discord) 177 | 178 | Clone each one using git 179 | 180 | ```cmd 181 | git clone https://github.com/vsTerminus/Discord-NP.git 182 | git clone https://github.com/vsTerminus/Mojo-Discord.git 183 | ``` 184 | 185 | Enter each folder and install the dependencies 186 | 187 | ```cmd 188 | cd Mojo-Discord 189 | cpanm --installdeps . 190 | cd ../Discord-NP 191 | cpanm --installdeps . 192 | ``` 193 | 194 | Next grab a copy of the "Mojo" folder (inside /lib) and drop it into `C:\Strawberry\perl\lib\` and choose Yes when it asks you if you would like to merge with the existing Net folder. 195 | 196 | Finally, make a copy of config.ini.example in the Discord-NP project folder and rename it to "config.ini". Fill it out as normal. 197 | 198 | To run it, `perl discordnp.pl` 199 | 200 | That's it! Have fun. 201 | 202 | ## Build From Source 203 | 204 | The build process is virtually identical for Mac, Windows, and Linux. 205 | 206 | You will need two things: 'make' and 'pp' 207 | 208 | 'pp' is a utility provided by PAR::Packer, a perl module which you can install using cpanminus like so: 209 | 210 | ```bash 211 | cpanm pp 212 | ``` 213 | 214 | 'make' is something you should install with your package manager. On macOS you can use brew, on Windows I recommend using [Chocolatey](https://chocolatey.org/). (Once installed you run `choco install make` in an admin terminal and that's it) 215 | 216 | Before you build, make sure you have a valid config.ini file in the project root *as well as in the 'build' folder*, I'll address that in a future update. The build process has to run the script to find dependencies, so having a valid config.ini is important. 217 | 218 | Finally, run 219 | 220 | ```bash 221 | make 222 | ``` 223 | 224 | and you should see it write the discordnp-OSTYPE file into the 'build' directory. 225 | 226 | # Troubleshooting 227 | 228 | Please, if you are having trouble open a ticket on the Issues tab. Let me know what's going on or what isn't clear so I can update it. 229 | 230 | You can also reach me on my discord, https://discord.gg/FuKTcHF 231 | 232 | I make no promises as to availability or time frames, but I will try to help you if I can. 233 | --------------------------------------------------------------------------------