Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Java-Twirk
[![](https://jitpack.io/v/Gikkman/Java-Twirk.svg)](https://jitpack.io/#Gikkman/Java-Twirk)
You can contact us via the [Discord Server](https://discord.gg/8NXaEyV)

Small, library for creating an IRC connection to the Twitch chat.

The library is intended to make communication via Twitch chat as easy as possible, and uses Java objects to represent most events that can occur in Twitch chat.
The library is intended to make communication via Twitch chat as easy as possible, and uses Java objects to represent
most events that can occur in Twitch chat.

Java 8 compatible.

Expand All @@ -21,14 +21,25 @@ Include the following in your pom.xml
<dependencies>
<dependency>
<groupId>com.github.gikkman</groupId>
<artifactId>Java-Twirk</artifactId>
<version>0.6.2</version>
<artifactId>Java-Twirk</artifactId>
<version>0.6.3</version>
</dependency>
</dependencies>
```
Or simply download the latest version of the library jar from the release page.

## Changes
### 0.6.3
Some pretty big changes behind the scenes, but they should be fully backwards compatible. Below is a list of changes:
* `Cheer.getImageURL(...)` should now return a proper URL. Fix #30
* You can now set a custom PING interval, for how often the connection should ping Twitch.
See `TwirkBuilder.setPingInterval` Fix #29
* You can now assign custom log methods to Twirk, in case you use some kind of logging framework. You can also set
different log levels, which gives a bit more control of what to log.
See `TwirkBuilder.setLogLevel` and`TwirkBuilder.setXXXLogMethod`. Fix #28 (thanks to PR #31).
* Calling connect after a disconnect should now work. Fix #26
* You don't need to include the '#' anymore in the channel name. Fix #21

### 0.6.2
Hotfix release since some emote IDs were still not parsed correctly (see #22). This hotfix should hopefully fix this issue.
Please report any further issues with parsing emotes.
Expand All @@ -40,12 +51,15 @@ There has only been minor changes between 0.5 and 0.6. Nothing that should break
* Fixed SockerClosedException stacktrace printing on some locales
* I think I fixed it at least, but this one is hard to test since there are so many locales.
* Updated the emotes parse for a safer and faster implementation.
* This deprecates a previously public method (`EmoteParse.parseEmote(String, String)`), and the new method is package private. There isn't really any need to call these methods from outside the library
* This deprecates a previously public method (`EmoteParse.parseEmote(String, String)`), and the new method is package
private. There isn't really any need to call these methods from outside the library
* Twirk will not only show the "User X was not online" or "User X was already online", when in verbose mode.
* This happened when we see a leave/part message but didn't track the user correctly. I felt like it was not needed unless you want the verbose output
* This happened when we see a leave/part message but didn't track the user correctly. I felt like it was not needed
unless you want the verbose output
* Updated the example a bit
* Started to move towards proper JUnit tests
* My home rolled test setup wasn't very user friendly, so now I started moving to user regular `@Test` tests. I'll eventually convert all tests to this format
* My home rolled test setup wasn't very user friendly, so now I started moving to user regular `@Test` tests. I'll
eventually convert all tests to this format

And probably some more...

Expand All @@ -68,21 +82,34 @@ message with a "pong USER_NAME".
} );
```

For a more complex example, which shows how to connect properly and how to write simple bot commands, check out the example code in `src/example/java`
For a more complex example, which shows how to connect properly and how to write simple bot commands, check out the
example code in `src/example/java`

#### Extendable
You can make Twirk use your own implementation of all event types by using custom builder classes. By extending the types Builder interface, and then passing an instance of your custom builder to the TwirkBuilder, you can use your own custom implementation of whichever type you want.
You can make Twirk use your own implementation of all event types by using custom builder classes. By extending the
types Builder interface, and then passing an instance of your custom builder to the TwirkBuilder, you can use your own
custom implementation of whichever type you want.
```Java
final Twirk twirk = new TwirkBuilder(channel, SETTINGS.MY_NICK, SETTINGS.MY_PASS)
.setClearChatBuilder( new MyClearChatBuilder() )
.build();
```
This will make the Twirk instance build instances of your custom implementation of `ClearChat` events

# Known / Verified bot
If your bot requires a very high message rate limit, you can request a verified bot account. It will increase the
bots message rate limits: https://dev.twitch.tv/docs/irc/guide#known-and-verified-bots
This isn't strictly necessary if you are just making a bot for your own account, as you might not reach the message
limits. For larger/more frequently used bots, it might
be necessary.

# Contribute
If you find any issues, or have suggestions for features (which does not clutter the library), feel free to submit an [Issue](https://github.com/Gikkman/Java-Twirk/issues) or make a pull request. You can also reach me on [Twitter](https://twitter.com/gikkman) or on [Twitch](http://twitch.com/gikkman)
If you find any issues, or have suggestions for features (which does not clutter the library), feel free to submit
an [Issue](https://github.com/Gikkman/Java-Twirk/issues) or make a pull request. You can also reach me
on [Twitter](https://twitter.com/gikkman) or on [Twitch](http://twitch.com/gikkman)


# License
This library is licensed under the [MIT License](https://tldrlegal.com/license/mit-license). If you use it, a link to this GitHub page is also greatly appriciated, if possible :)
This library is licensed under the [MIT License](https://tldrlegal.com/license/mit-license). If you use it, a link to
this GitHub page is also greatly appriciated, if possible :)

4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.gikk</groupId>
<artifactId>twirc</artifactId>
<version>0.6.2</version>
<version>0.6.3</version>
<packaging>jar</packaging>

<name>twirc</name>
Expand Down Expand Up @@ -97,7 +97,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
39 changes: 28 additions & 11 deletions src/example/java/com/gikk/twirk/BotExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.gikk.twirk.events.TwirkListener;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**Simple example of how Twirk can be used. <br><br>
Expand All @@ -22,29 +23,45 @@ public static void main(String[] args) throws IOException, InterruptedException{
System.out.println("Welcome to this Bot example. In this example you will be able \n"
+ "to send and receive messages from a Twitch chat channel. You will \n"
+ "make all input directly here in the command prompt. \n\n"
+ "Enter channel to join (leave out the #):");
Scanner scanner = new Scanner(new InputStreamReader(System.in, "UTF-8"));
String channel = "#" + scanner.nextLine();
+ "Enter channel to join:");
Scanner scanner = new Scanner(new InputStreamReader(System.in, StandardCharsets.UTF_8));
String channel = scanner.nextLine();

final Twirk twirk = new TwirkBuilder(channel, SETTINGS.MY_NICK, SETTINGS.MY_PASS)
.setVerboseMode(true) //We want to print everything we receive from Twitch
.build(); //Create the Twirk object
.setVerboseMode(true)
.build();

twirk.addIrcListener( getOnDisconnectListener(twirk) );
twirk.addIrcListener( new PatternCommandExample(twirk) );
twirk.addIrcListener( new PrefixCommandExample(twirk) );

System.out.println("\nTo exit this example, type .quit and press Enter\n");
System.out.println("To send a message to the channel, type it in the console and press Enter");
System.out.println("To reconnect to Twitch, type .reconnect and press Enter");
System.out.println("To exit this example, type .quit and press Enter");

Thread.sleep(2000);
twirk.connect(); //Connect to Twitch

//As long as we don't type .quit into the command prompt, send everything we type as a message to twitch
String line;
while( !(line = scanner.nextLine()).matches(".quit") )
twirk.channelMessage(line);

while( (line = scanner.nextLine()) != null ) {
if(".quit".equals(line)) {
//Close the connection to Twitch, and release all resources. This will not fire the onDisconnect
//method
twirk.close();
break;
}
else if(".reconnect".equals(line)) {
//Close the connection to Twitch, and release all resources. This will fire the onDisconnect method
//however, which will cause us to reconnect to Twitch.
twirk.disconnect();
}
else {
twirk.channelMessage(line);
}
}

scanner.close(); //Close the scanner
twirk.close(); //Close the connection to Twitch, and release all resources
}

private static TwirkListener getOnDisconnectListener(final Twirk twirk) {
Expand All @@ -62,7 +79,7 @@ public void onDisconnect() {
//If reconnection threw an IO exception, close the connection and release resources.
twirk.close();
}
catch (InterruptedException e) { }
catch (InterruptedException ignored) { }
}
};
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/gikk/twirk/InputThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public void run() {
try{
connection.incommingMessage(line);
} catch (Exception e) {
System.err.println("Error in handling the incomming Irc Message");
e.printStackTrace();
connection.logger.error("Error in handling the incoming Irc Message");
Util.printError(connection.logger, e);
}
}
//If we reach this line, it means the line was null. That only happens if the end of the stream's been reached
Expand All @@ -67,10 +67,10 @@ public void run() {
if( (message.toLowerCase().contains("socket closed")) ){
//Ignore
} else if ( message.contains("connection reset") || message.contains("stream closed")) {
System.err.println( message );
connection.logger.warn( e.getMessage() );
}
else {
e.printStackTrace();
Util.printError(connection.logger, e);
}
isConnected = false;
}
Expand All @@ -81,7 +81,7 @@ public void run() {
* inner catch-blocks. That should be very rare, but can occur if the thread is interrupted while
* handling another exception
*/
e.printStackTrace();
Util.printError(connection.logger, e);
}

//If we have been disconnected, we close the connection and clean up the resources held by the IrcConnection.
Expand Down
30 changes: 12 additions & 18 deletions src/main/java/com/gikk/twirk/OutputThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,10 @@ public void run(){
/**Circumvents the message queue completely and attempts to send the message at once. Should only be used for sending
* PING responses.
*
* @param message
* @param message the message to send
*/
public void quickSend(String message) {
try {
sendLine(message);
} catch (SocketException e) {
System.err.println("Could not QuickSend message. Socket was closed (OutputThread @ Twirk)");
}
sendLine(message);
}

/**Tells the thread to stop execution. Future messages written to the {@code OutputQueue} will
Expand Down Expand Up @@ -104,23 +100,20 @@ void setMessageDelay(int millis){
//***********************************************************************************************

/**Sends a message AS IS, without modifying it in any way. Users of this method are responsible for
* formating the string correctly:
* formatting the string correctly:
* <br>
* That means, who ever uses this method has to manually assign channel data and the similar to the
* message.
*
* @param message The message to write to out BufferedWriter
*/
private void sendLine(String message) throws SocketException{
private void sendLine(String message) {
if( !isConnected ){
System.err.println("Twirk is not connected! Sending messages will not succeed!");
connection.logger.error("Twirk is not connected! Sending messages will not succeed!");
}
connection.logger.debug("OUT " + message);

if(connection.verboseMode) {
System.out.println("OUT " + message);
}

/**An IRC message may not be longer than 512 characters. Also, they must end with \r\n,
/* An IRC message may not be longer than 512 characters. Also, they must end with \r\n,
* so if the supplied message is longer than 510 characters, we have to cut it short.
*
* While it might be an alternative to split the message and send it in different batches,
Expand All @@ -137,11 +130,12 @@ private void sendLine(String message) throws SocketException{
writer.flush();
}
} catch (IOException e){
if( e.getMessage().matches("Stream closed") ){
System.err.println("Cannot send message: " + message +" Stream closed");
return;
if( e.getMessage().toLowerCase().matches("stream closed") ){
connection.logger.warn("Cannot send message: \"" + message + "\" Stream closed");
}
else {
Util.printError(connection.logger, e);
}
e.printStackTrace();
}
}
}
18 changes: 16 additions & 2 deletions src/main/java/com/gikk/twirk/SocketFactory.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package com.gikk.twirk;

import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.Socket;

/**
/** Factory method for creating a {@link Socket} for the Twirk instance. The socket is later used to connect to Twitch
*
* @author Gikkman
*/
@FunctionalInterface
public interface SocketFactory {
public Socket createSocket() throws IOException;

/**
* Given a server and a port, creates {@link Socket} that can be used to connect to Twitch's IRC interface
* @param server the server
* @param port the port
* @return a {@link Socket}
* @throws IOException if a {@link Socket} cannot be created
*/
Socket createSocket(String server, int port) throws IOException;

static SocketFactory getDefault(boolean useSSL) {
return useSSL ? SSLSocketFactory.getDefault()::createSocket : Socket::new;
}
}
Loading