This commit is contained in:
Kwoth 2016-08-01 22:22:53 +02:00
commit 71b3385509
14 changed files with 359 additions and 131 deletions

View File

@ -1,65 +1,112 @@
________________________________________________________________________________ ________________________________________________________________________________
*Thanks to @Flatbread for making this guide* *Thanks to @Flatbread and Mirai for making this guide*
________________________________________________________________________________ ________________________________________________________________________________
#### Setting Up NadekoBot v0.98 ### Setting Up NadekoBot on Windows
###### Prerequisites: #### Prerequisites
1) NET Framework 4.5.2 (or 4.6) - 1) [NET Framework][NET Framework] 4.5.2 (or 4.6)
- Start with making a folder, lets name it `Nadeko` - 2) [FFMPEG][FFMPEG]
- Make sure you have **7zip** installed, if not then head to http://www.7-zip.org/download.html and download/install it. - 3) Google Account
- Now head to https://github.com/Kwoth/NadekoUpdater/releases/tag/v1.0 and download `WINDOWS.-.nadeupdater.7z - 4) Soundcloud Account (if you want soundcloud support)
- *Alternatively, you can download nadekobot from [releases](https://github.com/Kwoth/NadekoBot/releases) and extract the zip yourself. That is what updater does, except it makes it easier for you to update because it doesn't overwrite important files. If you are downloading releases you will have to be careful about your config, credentials, and other files you edited in order to preserve your data every time you update.* - 5) [7zip][7zip] (or whatever you are using, WinRar)
- 6) [Notepad++][Notepad++]
####Guide:
- Create a folder, name it `Nadeko`.
- Head to [Releases][Releases]* and download `WINDOWS.-.nadeupdater.7z`.
- Copy `WINDOWS.-.nadeupdater.7z` to the `Nadeko` (folder we created before) and extract everything. - Copy `WINDOWS.-.nadeupdater.7z` to the `Nadeko` (folder we created before) and extract everything.
- You will see a file `NadekoUpdater.bat ` and a folder `publish ` after extraction. - You will see a file `NadekoUpdater.bat ` and a folder `publish ` after extraction.
- Run/Launch/Open the file `NadekoUpdater.bat ` and you will see it running in cmd.exe asking you with **3 options** *1-3*. - Run/Launch/Open the file `NadekoUpdater.bat ` and you will see it running in cmd.exe asking you with **3 options** *1-3*.
- You can try the stable release, but its better to get the newest release if you want all features/upgrades. So for that press `2 ` and hit `Enter ` (read everything shows up on screen) - 1) Stable release - current stable release, but might not contain all the newest Nadeko updates.
- It should ask you `Are you sure you want to update?` and for that type `y ` and hit `Enter ` - 2) Newest release - release with all features/upgrades.
- It should complete downloading (might take a while) and you can type `3 ` and hit `Enter ` and close it. - 3) Exit
- You should now see a new folder `NadekoBot ` inside our `Nadeko ` folder. - Press `2` on your keyboard and hit `Enter`. Type `y` and hit `Enter` again. Downloading might take a while, so just be patient and wait. When download is done, press `3` on your keyboard and close the updater.
- Open it and head over to https://github.com/Kwoth/NadekoBot/blob/master/README.md to setup credentials or just check it out for your knowledge. - You should have a new folder named `NadekoBot` inside the `Nadeko` folder we previously created.
- **For Basic Credentials Setup:**
- Rename `credentials_example.json ` into `credentials.json ` (Note: If you do not see a **.json** after `credentials_example.json `, do not add the **.json**. `You are most likely to have "Hide file extensions" as enabled. `)
- **Go to:** http://discord.kongslien.net/guide.html **for detailed guidance with screenshots.** or :arrow_down:
- **Follow for textual guidelines:** Go here https://discordapp.com/developers/applications/me
- Log in with your Discord account. Press **New Application** and fill out an **App Name** and, optionally, an app description and icon. Afterwards, create the application. Once the application is created, click on **Create a Bot User** and confirm it. You will then see the bot's username, ID and token. Reveal and copy the **token** and the **bot ID** *(one by one xD)*.
- Open up `credentials.json` with **NotePad++** Paste the **token** into the **Token** field, between the `"quotes"`. Paste the **Bot ID** into the **BotID** field, when done, *Save* and *close* `credentials.json`
- **To Invite your bot to your server:**
- Copy your CLIENT ID (that's in the same Developer page where you brought your token) and replace `12345678` in this link:
`https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303 ` with it. Go to that link and you will be able to add your bot to your server.
- Start NadekoBot.exe. In a text channel, **not a direct message**, type in [.uid @______] without the brackets, filling in the underlined portion with your name and send the message. Your bot will reply with a number; this is your ID. Copy this ID and close NadekoBot.exe.
- Reopen credentials.json. Paste your ID into the square brackets ("OwnerIds": [1231312313]). **Just paste/replace your ID with existing and keep the default format intact, you don't need to add extra space or anything else.** You can add multiple owners by separating IDs with a comma. Close and save credentials.json.
####Creating DiscordBot application
- Go to [DiscordApp][DiscordApp].
- Log in with your Discord account.
- On the left side, press `New Application`.
- Fill out the `App Name` (your bot's name, in this case), put the image you want, and add an app description(optional).
- Create the application.
- Once the application is created, click on `Create a Bot User` and confirm it.
- Keep this window open for now.
####Setting up Credentials.json file
- In our `NadekoBot` folder you should have `.json` file named `credentials_example.json`. (Note: If you do not see a **.json** after `credentials_example.json `, do not add the `**.json**`. You most likely have `"Hide file extensions"` enabled.)
- Rename `credentials_example.json` to `credentials.json`.
- Open the file with your [Notepad++][Notepad++].
- In there you will see fields like `Token`, `ClientId`, `BotId`, `OwnerIDs`.
- In your [DiscordApp][DiscordApp], under `Bot User` part, you will see the `Token:click to reveal` part, click to reveal it.
- Copy your bot's token, and put it between `" "` in your `credentials.json` file.
- Copy `Client ID` and replace it with the example one in your `credentials.json`.
- Copy `Bot ID` and replace it with the example one in your `credentials.json`.
- Save your `credentials.json` but keep it open. We need to put your `User ID` and owner.
####Inviting your bot to your server [Invite Guide][Invite Guide]
- Create a new server in Discord.
- Copy your `Client ID` from your [DiscordApp][DiscordApp].
- Replace `12345678` in this link `https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303` with your `Client ID`.
- Link should look like this: `https://discordapp.com/oauth2/authorize?client_id=**YOUR_CLENT_ID**&scope=bot&permissions=66186303`.
- Go to newly created link and pick the server we created, and click `Authorize`.
- Bot should be added to your server.
####Starting the bot
- Enter your `NadekoBot` folder that should be (hopefully) in your `Nadeko` folder.
- Run `NadekoBot.exe` (Note: There is `NadekoBot.exe` and `NadekoBot.exe.config`, dont run the second one)
- Your bot should now be online in the server we added him to.
- Note: Your bot will be offline in case you close `NadekoBot.exe`.
####Setting up OwnerIds:
- In the server where your bot is, in a text channel, type `.uid`
- Your `User ID` should show, copy it.
- Close `NadekoBot.exe`
- Replace your `User ID` in the `credentials.json` between `[ ]` and save the changes.
- Run `NadekoBot.exe` again.
- Now you are the bot owner.
- You can add `User IDs` from the other users by separating IDs with a comma if you want to have more owners.
`*Alternatively, you can download nadekobot from [Releases][Releases] and extract the zip yourself. That is what updater does, except it makes it easier for you to update because it doesn't overwrite important files. If you are downloading releases you will have to be careful about your config, credentials, and other files you edited in order to preserve your data every time you update.`
________________________________________________________________________________ ________________________________________________________________________________
#### Setting Up NadekoBot For Music #### Setting Up NadekoBot For Music
###### Setting up `ffmpeg` with installer: ##### Prerequisites
1) Google Account - 1) [FFMPEG][FFMPEG] installed.
2) Soundcloud Account (if you want soundcloud support) - 2) Setting up API keys.
3) Download installer here: https://goo.gl/lQZnsH (pick the one for your system, either 32 or 64bit and then click 'download')
4) Run the installer
5) Follow these steps on how to setup API keys: - Follow these steps on how to setup Google API keys:
- Go to https://console.developers.google.com and log in. - Go to [Google Console][Google Console] and log in.
- Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs." - Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs."
- Under the "Other Popular APIs" section, enable "URL Shortener API". Under the "YouTube APIs" section, enable "YouTube Data API". Also enable Custom Search Api. - Under the "Other Popular APIs" section, enable `URL Shortener API` and `Custom Search Api`. Under the `YouTube APIs` section, enable `YouTube Data API`.
- On the left tab, access Credentials. There will be a line saying "If you wish to skip this step and create an API key, client ID or service account." Click on API Key, and then Server Key in the new window that appears. Enter in a name for the server key. A new window will appear with your Google API key. Copy the key. - On the left tab, access `Credentials`. Click `Create Credentials` button. Click on `API Key`, and then `Server Key` in the new window that appears. Enter in a name for the `Server Key`. A new window will appear with your `Google API key`.
- Open up credentials.json. For "GoogleAPIKey", fill in with the new key. - Copy the key.
- Go to (https://soundcloud.com/you/apps/new). Enter a name for the app and create it. You will see a page with the title of your app, and a field labeled Client ID. Copy the ID. In credentials.json, fill in "SoundcloudClientID" with the copied ID. - Open up `credentials.json`.
- For `"GoogleAPIKey"`, fill in with the new key we copied.
- Follow these steps on how to setup Soundcloud API key:
- Go to [Soundcloud][Soundcloud].
- Enter a name for the app and create it.
- You will see a page with the title of your app, and a field labeled `Client ID`. Copy the ID.
- In `credentials.json`, fill in `"SoundcloudClientID"` with the copied ID.
- Restart your computer. - Restart your computer.
###### Prerequisites for manual `ffmpeg` setup: ##### Prerequisites for manual `ffmpeg` setup:
1) Google Account **Do this step in case you were not able to install `ffmpeg` with the installer.**
2) Soundcloud Account (if you want soundcloud support)
- Create a folder named `ffmpeg` in your main Windows directory. We will use **C:\ffmpeg** (for our guide) - Create a folder named `ffmpeg` in your main Windows directory. We will use **C:\ffmpeg** (for our guide)
- Download FFMPEG through the link https://ffmpeg.zeranoe.com/builds/ (download static build) - Download FFMPEG through the link https://ffmpeg.zeranoe.com/builds/ (download static build)
- Extract it using `7zip` and place the folder `ffmpeg-xxxxx-git-xxxxx-xxxx-static` inside **C:\ffmpeg** - Extract it using `7zip` and place the folder `ffmpeg-xxxxx-git-xxxxx-xxxx-static` inside **C:\ffmpeg**
- Before proceeding, check out this gif to set up `ffmpeg` PATH correctly http://i.imgur.com/aR5l1Hn.gif *(thanks to PooPeePants#7135)* - Before proceeding, check out this gif to set up `ffmpeg` PATH correctly http://i.imgur.com/aR5l1Hn.gif *(thanks to PooPeePants#7135)*
- Go to My Computer, right click and select Properties. On the left tab, select Advanced System Settings. Under the Advanced tab, select Environmental Variables near the bottom. One of the variables should be called "Path". Add a semi-colon (;) to the end followed by your FFMPEG's **bin** install location (**for example C:\ffmpeg\ffmpeg-xxxxx-git-xxxxx-xxxx-static\bin**). Save and close. - Go to My Computer, right click and select Properties. On the left tab, select Advanced System Settings. Under the Advanced tab, select Environmental Variables near the bottom. One of the variables should be called "Path". Add a semi-colon (;) to the end followed by your FFMPEG's **bin** install location (**for example C:\ffmpeg\ffmpeg-xxxxx-git-xxxxx-xxxx-static\bin**). Save and close.
- Go to https://console.developers.google.com and log in. - Setup your API keys as explained above.
- Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs."
- Under the "Other Popular APIs" section, enable "URL Shortener API". Under the "YouTube APIs" section, enable "YouTube Data API". Also enable Custom Search Api.
- On the left tab, access Credentials. There will be a line saying "If you wish to skip this step and create an API key, client ID or service account." Click on API Key, and then Server Key in the new window that appears. Enter in a name for the server key. A new window will appear with your Google API key. Copy the key.
- Open up credentials.json. For "GoogleAPIKey", fill in with the new key.
- Go to (https://soundcloud.com/you/apps/new). Enter a name for the app and create it. You will see a page with the title of your app, and a field labeled Client ID. Copy the ID. In credentials.json, fill in "SoundcloudClientID" with the copied ID.
- Restart your computer. - Restart your computer.
[NET Framework]: https://www.microsoft.com/en-us/download/details.aspx?id=48130
[FFMPEG]: https://github.com/Soundofdarkness/FFMPEG-Installer
[7zip]: http://www.7-zip.org/download.html
[Releases]: //github.com/Kwoth/NadekoUpdater/releases/tag/v1.0
[DiscordApp]: https://discordapp.com/developers/applications/me
[Notepad++]: https://notepad-plus-plus.org/
[Invite Guide]: http://discord.kongslien.net/guide.html
[Google Console]: https://console.developers.google.com
[Soundcloud]: https://soundcloud.com/you/apps/new

53
DockerGuide.md Normal file
View File

@ -0,0 +1,53 @@
## Docker guide with digitalocean
#####Prerequisites:
- Digital ocean account (you can use my [reflink][reflink] to support the project and get 10$ after you register)
- [PuTTY][PuTTY]
- A bot account - follow this [guide][guide]
- $5
- Common sense
#####Guide
- Click on the create droplet button
![img](http://i.imgur.com/g2ayOcC.png)
- Pick one click apps and select docker on 14.04
![img](http://imgur.com/065Xkme.png)
- Pick any droplet size you want (5$ will work ok-ish on a few servers)
- Pick location closest to your discord server's location
- Pick a hostname
![img](http://imgur.com/ifPKB6p.png)
- Click create
You will get an email from DigitalOcean with your credentials now.
Open putty and type ip adress **you got in your email** with port 22
![img](http://imgur.com/Mh5ehsh.png)
- Console will open and you will be prompted for a username, type `root`.
- Type in the password you got in the email.
- Confirm the password you just typed in.
- Type in the new password.
- Confirm new password.
- When you are successfully logged in, type
`docker run --name nadeko -v /nadeko:/config uirel/nadeko`
- Wait for it to download and at one point it is going to start throwing errors due to `credentials.json` being empty
- CTRL+C to exit that
- Type `docker stop nadeko`
- Type `nano /nadeko/credentials.json` and type in your `credentials`
- CTRL+X then CTRL+Y to save
- Type `docker start nadeko`
**Your bot is running, enjoy! o/**
*When you want to update the bot, just type `docker restart nadeko` as it always downloads latest prerelease*
[reflink]: http://m.do.co/c/46b4d3d44795/
[PuTTY]: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
[guide]: http://discord.kongslien.net/guide.html

View File

@ -26,10 +26,12 @@ If you entered your Droplets IP address correctly, it should show **login as:**
######MONO (Source: [Mono Source][Mono Source]) ######MONO (Source: [Mono Source][Mono Source])
**1)** **1) Installing Mono**
`sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF` `sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF`
`echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list` `echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list`
`sudo apt-get update` `sudo apt-get update`
Note if the command is not being initiated, hit **Enter** Note if the command is not being initiated, hit **Enter**
@ -47,7 +49,9 @@ Note if the command is not being initiated, hit **Enter**
*ONLY CentOS 7, Fedora 19 (and later)* *ONLY CentOS 7, Fedora 19 (and later)*
`yum install yum-util` `yum install yum-util`
`rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF"` `rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF"`
`yum-config-manager --add-repo http://download.mono-project.com/repo/centos/` `yum-config-manager --add-repo http://download.mono-project.com/repo/centos/`
**3)** **3)**
@ -71,8 +75,11 @@ Opus Voice Codec
**In case you are having issues with Mono where you get a random string and the bot won't run, do this:** **In case you are having issues with Mono where you get a random string and the bot won't run, do this:**
`sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF` `sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF`
`echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list` `echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list`
`apt-get install ca-certificates-mono` `apt-get install ca-certificates-mono`
`mozroots --import --sync` `mozroots --import --sync`

View File

@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
cgb.CreateCommand(Module.Prefix + "autoassignrole") cgb.CreateCommand(Module.Prefix + "autoassignrole")
.Alias(Module.Prefix + "aar") .Alias(Module.Prefix + "aar")
.Description($"Automaticaly assigns a specified role to every user who joins the server. Type `{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable") .Description($"Automaticaly assigns a specified role to every user who joins the server. |`{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable")
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.AddCheck(new SimpleCheckers.ManageRoles()) .AddCheck(new SimpleCheckers.ManageRoles())
.Do(async e => .Do(async e =>

View File

@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "repeatinvoke") cgb.CreateCommand(Module.Prefix + "repeatinvoke")
.Alias(Module.Prefix + "repinv") .Alias(Module.Prefix + "repinv")
.Description("Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`") .Description($"Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`")
.AddCheck(SimpleCheckers.ManageMessages()) .AddCheck(SimpleCheckers.ManageMessages())
.Do(async e => .Do(async e =>
{ {

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "betray") cgb.CreateCommand(Module.Prefix + "betray")
.Description("BETRAY GAME. Betray nadeko next turn." + .Description("BETRAY GAME. Betray nadeko next turn." +
"If Nadeko cooperates - you get extra points, nadeko loses a LOT." + "If Nadeko cooperates - you get extra points, nadeko loses a LOT." +
"If Nadeko betrays - you both lose some points. | `{Prefix}betray`") $"If Nadeko betrays - you both lose some points. | `{Prefix}betray`")
.Do(async e => .Do(async e =>
{ {
await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false); await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false);
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "cooperate") cgb.CreateCommand(Module.Prefix + "cooperate")
.Description("BETRAY GAME. Cooperate with nadeko next turn." + .Description("BETRAY GAME. Cooperate with nadeko next turn." +
"If Nadeko cooperates - you both get bonus points." + "If Nadeko cooperates - you both get bonus points." +
"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`") $"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`")
.Do(async e => .Do(async e =>
{ {

View File

@ -80,7 +80,7 @@ $@"######For more information and how to setup your own NadekoBot, go to: <http:
{ {
cgb.CreateCommand(Module.Prefix + "h") cgb.CreateCommand(Module.Prefix + "h")
.Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h") .Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h")
.Description("Either shows a help for a single command, or PMs you help link if no arguments are specified. | `-h !m q` or just `-h` ") .Description($"Either shows a help for a single command, or PMs you help link if no arguments are specified. | `{Prefix}h !m q` or just `{Prefix}h` ")
.Parameter("command", ParameterType.Unparsed) .Parameter("command", ParameterType.Unparsed)
.Do(HelpFunc()); .Do(HelpFunc());
cgb.CreateCommand(Module.Prefix + "hgit") cgb.CreateCommand(Module.Prefix + "hgit")
@ -102,7 +102,7 @@ $@"######For more information and how to setup your own NadekoBot, go to: <http:
cgb.CreateCommand(Module.Prefix + "donate") cgb.CreateCommand(Module.Prefix + "donate")
.Alias("~donate") .Alias("~donate")
.Description("Instructions for helping the project! | `{Prefix}donate` or `~donate`") .Description($"Instructions for helping the project! | `{Prefix}donate` or `~donate`")
.Do(async e => .Do(async e =>
{ {
await e.Channel.SendMessage( await e.Channel.SendMessage(

View File

@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Help
cgb.CreateCommand(Prefix + "modules") cgb.CreateCommand(Prefix + "modules")
.Alias(".modules") .Alias(".modules")
.Description("List all bot modules. | `{Prefix}modules` or `.modules`") .Description($"List all bot modules. | `{Prefix}modules` or `.modules`")
.Do(async e => .Do(async e =>
{ {
await e.Channel.SendMessage("`List of modules:` \n• " + string.Join("\n• ", NadekoBot.Client.GetService<ModuleService>().Modules.Select(m => m.Name)) + $"\n`Type \"{Prefix}commands module_name\" to get a list of commands in that module.`") await e.Channel.SendMessage("`List of modules:` \n• " + string.Join("\n• ", NadekoBot.Client.GetService<ModuleService>().Modules.Select(m => m.Name)) + $"\n`Type \"{Prefix}commands module_name\" to get a list of commands in that module.`")
@ -38,7 +38,7 @@ namespace NadekoBot.Modules.Help
cgb.CreateCommand(Prefix + "commands") cgb.CreateCommand(Prefix + "commands")
.Alias(".commands") .Alias(".commands")
.Description("List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`") .Description($"List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`")
.Parameter("module", ParameterType.Unparsed) .Parameter("module", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {

View File

@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes
public IReadOnlyCollection<Song> Playlist => playlist; public IReadOnlyCollection<Song> Playlist => playlist;
public Song CurrentSong { get; private set; } public Song CurrentSong { get; private set; }
private CancellationTokenSource SongCancelSource { get; set; } public CancellationTokenSource SongCancelSource { get; private set; }
private CancellationToken cancelToken { get; set; } private CancellationToken cancelToken { get; set; }
public bool Paused { get; set; } public bool Paused { get; set; }

View File

@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Music.Classes
public SongInfo SongInfo { get; } public SongInfo SongInfo { get; }
public string QueuerName { get; set; } public string QueuerName { get; set; }
private bool bufferingCompleted { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; } public MusicPlayer MusicPlayer { get; set; }
public string PrettyCurrentTime() public string PrettyCurrentTime()
@ -73,78 +72,22 @@ namespace NadekoBot.Modules.Music.Classes
return this; return this;
} }
private Task BufferSong(string filename, CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
try
{
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-ss {skipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true,
});
var prebufferSize = 100ul.MiB();
using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
{
byte[] buffer = new byte[81920];
int bytesRead;
while ((bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) != 0)
{
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while ((ulong)outStream.Length - bytesSent > prebufferSize)
await Task.Delay(100, cancelToken);
}
}
bufferingCompleted = true;
}
catch (System.ComponentModel.Win32Exception) {
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
}
catch (Exception ex)
{
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
{
p.Kill();
}
catch { }
p.Dispose();
}
}
}, TaskCreationOptions.LongRunning);
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{ {
var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false); SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
bytesSent = 0; bytesSent = 0;
try try
{ {
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken); var attempt = 0;
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken)); var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
@ -162,7 +105,6 @@ Check the guides for your platform on how to setup ffmpeg correctly:
Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed); Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed);
const int blockSize = 3840; const int blockSize = 3840;
var attempt = 0;
byte[] buffer = new byte[blockSize]; byte[] buffer = new byte[blockSize];
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
@ -173,14 +115,31 @@ Check the guides for your platform on how to setup ffmpeg correctly:
{ {
bytesSent += (ulong)read; bytesSent += (ulong)read;
} }
if (read == 0) if (read < blockSize)
if (attempt++ == 20) {
if (sb.IsNextFileReady())
{ {
voiceClient.Wait(); inStream.Dispose();
break; inStream = new FileStream(sb.GetNextFile(), FileMode.Open, FileAccess.Read, FileShare.Write);
read += inStream.Read(buffer, read, buffer.Length - read);
attempt = 0;
}
if (read == 0)
{
if (sb.BufferingCompleted)
break;
if (attempt++ == 20)
{
voiceClient.Wait();
MusicPlayer.SongCancelSource.Cancel();
break;
}
else
await Task.Delay(100, cancelToken).ConfigureAwait(false);
} }
else else
await Task.Delay(100, cancelToken).ConfigureAwait(false); attempt = 0;
}
else else
attempt = 0; attempt = 0;
@ -195,14 +154,16 @@ Check the guides for your platform on how to setup ffmpeg correctly:
{ {
await bufferTask; await bufferTask;
await Task.Run(() => voiceClient.Clear()); await Task.Run(() => voiceClient.Clear());
inStream.Dispose(); if(inStream != null)
try { File.Delete(filename); } catch { } inStream.Dispose();
Console.WriteLine("l");
sb.CleanFiles();
} }
} }
private async Task CheckPrebufferingAsync(Stream inStream, CancellationToken cancelToken) private async Task CheckPrebufferingAsync(Stream inStream, SongBuffer sb, CancellationToken cancelToken)
{ {
while (!bufferingCompleted && inStream.Length < 2.MiB()) while (!sb.BufferingCompleted && inStream.Length < 2.MiB())
{ {
await Task.Delay(100, cancelToken); await Task.Delay(100, cancelToken);
} }

View File

@ -0,0 +1,159 @@
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Classes
{
/// <summary>
/// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
/// It also help for large music by deleting files that are already seen.
/// </summary>
class SongBuffer
{
public SongBuffer(string basename, SongInfo songInfo, int skipTo)
{
Basename = basename;
SongInfo = songInfo;
SkipTo = skipTo;
}
private string Basename;
private SongInfo SongInfo;
private int SkipTo;
private static int MAX_FILE_SIZE = 20.MiB();
private long FileNumber = -1;
private long NextFileToRead = 0;
public bool BufferingCompleted { get; private set;} = false;
private ulong CurrentBufferSize = 0;
public Task BufferSong(CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
FileStream outStream = null;
try
{
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true,
});
byte[] buffer = new byte[81920];
int currentFileSize = 0;
ulong prebufferSize = 100ul.MiB();
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
while (!p.HasExited) //Also fix low bandwidth
{
int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false);
if (currentFileSize >= MAX_FILE_SIZE)
{
try
{
outStream.Dispose();
}catch { }
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
currentFileSize = bytesRead;
}
else
{
currentFileSize += bytesRead;
}
CurrentBufferSize += Convert.ToUInt64(bytesRead);
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while (CurrentBufferSize > prebufferSize)
await Task.Delay(100, cancelToken);
}
BufferingCompleted = true;
}
catch (System.ComponentModel.Win32Exception)
{
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
}
catch (Exception ex)
{
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
if(outStream != null)
outStream.Dispose();
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
{
p.Kill();
}
catch { }
p.Dispose();
}
}
}, TaskCreationOptions.LongRunning);
/// <summary>
/// Return the next file to read, and delete the old one
/// </summary>
/// <returns>Name of the file to read</returns>
public string GetNextFile()
{
string filename = Basename + "-" + NextFileToRead;
if (NextFileToRead != 0)
{
try
{
CurrentBufferSize -= Convert.ToUInt64(new FileInfo(Basename + "-" + (NextFileToRead - 1)).Length);
File.Delete(Basename + "-" + (NextFileToRead - 1));
}
catch { }
}
NextFileToRead++;
return filename;
}
public bool IsNextFileReady()
{
return NextFileToRead <= FileNumber;
}
public void CleanFiles()
{
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
{
try
{
File.Delete(Basename + "-" + i);
}
catch { }
}
}
}
}

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Translator
internal override void Init(CommandGroupBuilder cgb) internal override void Init(CommandGroupBuilder cgb)
{ {
cgb.CreateCommand(Module.Prefix + "translangs") cgb.CreateCommand(Module.Prefix + "translangs")
.Description("List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`") .Description($"List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`")
.Parameter("search", ParameterType.Optional) .Parameter("search", ParameterType.Optional)
.Do(ListLanguagesFunc()); .Do(ListLanguagesFunc());
} }

View File

@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Trello
}); });
cgb.CreateCommand(Prefix + "unbind") cgb.CreateCommand(Prefix + "unbind")
.Description("Unbinds a bot from the channel and board.") .Description($"Unbinds a bot from the channel and board. | `{Prefix}unbind`")
.Do(async e => .Do(async e =>
{ {
if (!NadekoBot.IsOwner(e.User.Id)) return; if (!NadekoBot.IsOwner(e.User.Id)) return;
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Trello
cgb.CreateCommand(Prefix + "lists") cgb.CreateCommand(Prefix + "lists")
.Alias(Prefix + "list") .Alias(Prefix + "list")
.Description("Lists all lists yo ;)") .Description($"Lists all lists yo ;) | {Prefix}list")
.Do(async e => .Do(async e =>
{ {
if (!NadekoBot.IsOwner(e.User.Id)) return; if (!NadekoBot.IsOwner(e.User.Id)) return;

View File

@ -231,6 +231,7 @@
<Compile Include="Modules\Music\Classes\MusicControls.cs" /> <Compile Include="Modules\Music\Classes\MusicControls.cs" />
<Compile Include="Modules\Music\Classes\PoopyBuffer.cs" /> <Compile Include="Modules\Music\Classes\PoopyBuffer.cs" />
<Compile Include="Modules\Music\Classes\Song.cs" /> <Compile Include="Modules\Music\Classes\Song.cs" />
<Compile Include="Modules\Music\Classes\SongBuffer.cs" />
<Compile Include="Modules\Music\Classes\SoundCloud.cs" /> <Compile Include="Modules\Music\Classes\SoundCloud.cs" />
<Compile Include="Modules\Permissions\Classes\PermissionChecker.cs" /> <Compile Include="Modules\Permissions\Classes\PermissionChecker.cs" />
<Compile Include="Modules\Permissions\Classes\PermissionHelper.cs" /> <Compile Include="Modules\Permissions\Classes\PermissionHelper.cs" />