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
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Small, basic 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.

Java 6 compatible.
Java 8 compatible.

## Installation
Include the following in your pom.xml
Expand All @@ -30,14 +30,18 @@ Include the following in your pom.xml
Or simply download the latest version of the library jar from the release page.

## Changes
There has been some changes between Java-Twirk 0.2 and Java-Twirk 0.3:
* TwitchUser.getUserId() now returns long (was int)
* TwitchUser.getName() is now called TwitchUser.getDisplayName()
* TwitchUser.getUserName() added (retrieves the users login name)
* USER_TYPE now has type SUBSCRIBER
* USER_TYPE.value changed for most types
* TwirkListener.onWhisper is now depricated (Twitch uses a new whisper system)
* TwirkListener.onMode is now depricated (User TwitchUser.isMod() instead)
There has beenmajor changes from Java-Twirk 0.3 to Java-Twirk 0.4. I am sorry for the hassle, but it was a necessity. Changes include (but is not limited to)
* Added support for Subscription Gift
* Added support for Raid
* Encapsulated all default factories (previously all public under GikkDefault_<NAME HERE>)
* TwirkListenerBaseImpl has been removed, please implement TwirkListener instead
* Mode-events has been depricated (please use the TwitchUser.isMod() instead)
* Subscriber-event has been removed. It is now found under Usernotice-event instead (Twitch has changed their grouping)
* USER_TYPES now has category SUBSCRIBER, which is given to regular subs and turbo subs.
* USER_TYPES now has ranks, which allows you to do if( user.getUserType().value >= USER_TYPE.SUBSCRIBER.value )
* Added the isOwner() status on TwichUser

And probably some more. Thankfully, Java is a strongly typed language :D

# Usage
#### Basic usage
Expand All @@ -51,7 +55,7 @@ There has been some changes between Java-Twirk 0.2 and Java-Twirk 0.3:
All events which can be reacted to are listed in [TwirkListener.java](https://github.com/Gikkman/Java-Twirk/blob/master/twirc/src/main/java/com/gikk/twirk/events/TwirkListener.java) This snippet will make your bot respond to any channel
message with a "pong USER_NAME".
```Java
twirk.addIrcListener( new TwirkListenerBaseImpl() {
twirk.addIrcListener( new TwirkListener() {
public void onPrivMsg( TwitchUser sender, TwitchMessage message) {
twirk.channelMessage("pong " + sender.getDisplayName() );
}
Expand All @@ -64,10 +68,10 @@ For a more complex example, which shows how to connect properly and how to write
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)
.setSubscriberEventBuilder( new MySubscriberEventBuilder() )
.setClearChatBuilder( new MyClearChatBuilder() )
.build();
```
This will make the Twirk instance build instances of your custom implementation of `SubscriberEvent`
This will make the Twirk instance build instances of your custom implementation of `ClearChat` events

# 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)
Expand Down
12 changes: 6 additions & 6 deletions twirc/pom.xml → 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.3</version>
<version>0.4</version>
<packaging>jar</packaging>

<name>twirc</name>
Expand All @@ -19,8 +19,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<build>
Expand All @@ -32,13 +32,13 @@
</resources>

<plugins>
<plugin> <!-- Compile java 6 compatible bytecode -->
<plugin> <!-- Compile java 8 compatible bytecode -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin> <!-- Create sources.jar -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@
import java.util.LinkedList;

/**We need a specialized messaging queue to be able to handle <ul>
* <li>A) one consumer/multiple producers and
* <li>A) one consumer/multiple producers and
* <li>B) being able to put messages to the front and back of the queue.
*</ul>
* We also want the {@link #next()} method to block until there is anything to send to the IRC server in the
* We also want the {@link #next()} method to block until there is anything to send to the IRC server in the
* queue.<br><br>
*
*
* Due to these reasons, we cannot use a normal queue. Thus we use this thread safe and blocking implementation.
*
*
* @author Gikkman
*
*/
class OutputQueue {
//***********************************************************************************************
// VARIABLES
//***********************************************************************************************
private final LinkedList<String> queue = new LinkedList<String>();
private final LinkedList<String> queue = new LinkedList<>();

//***********************************************************************************************
// PUBLIC
//***********************************************************************************************
/**Adds a message to the back of the output queue
*
*
* @param s The message to add to the queue
*/
public void add(String s){
Expand All @@ -33,10 +33,10 @@ public void add(String s){
queue.notify();
}
}

/**Adds a message to the front of the output queue.<br>
* Can be useful for prioritized messages
*
*
* @param s The message to add to the queue
*/
public void addFirst(String s){
Expand All @@ -45,42 +45,43 @@ public void addFirst(String s){
queue.notify();
}
}

/**A <b>blocking</b> call that retrieves the next message from the queue.
* If no message is currently in the queue, this method will block until a message appears.
*
*
* @return The next message OR <code>null</code>(if we were interrupted and there were no message in the queue)
*/
public String next(){
synchronized (queue) {
if( !hasNext() ){
try { queue.wait(); }
catch (InterruptedException e) {
try { queue.wait(); }
catch (InterruptedException e) {
/* Being interrupted either means that there now is an element in the queue or
* that the application is shutting down.
* Anyway, we don't need to care about being interrupted, we simply proceed as
* usual and let the thread waiting for input handle the potential null return */
* usual and let the thread waiting for input handle the potential null return */
}
}
if( !hasNext() )
return null;

}
if( !hasNext() ) {
return null;
}

String message = queue.getFirst();
queue.removeFirst();
return message;
}
}

/**Checks if there are any elements currently in the queue
*
*
* @return {@code true} if there are any messages in the queue
*/
public boolean hasNext(){
synchronized (queue) {
return queue.size() > 0;
}
}

/**This will cause all threads waiting for new content in the {@code queue} to wake up. <br>
* If there is no content when this call is issued, waiting threads will return {@code null}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import java.net.SocketException;

/**This class handles all outgoing IRC traffic.<br><br>
*
* The implementation is intended to be thread safe and handle all potential errors (<u>keyword: INTENDED</u>).
*
* The implementation is intended to be thread safe and handle all potential errors (<u>keyword: INTENDED</u>).
* That means that we can have multiple threads feeding the message queue safely and still operate without any trouble.
*
*
* @author Simon
*
*/
Expand All @@ -20,22 +20,22 @@ class OutputThread extends Thread{
private final Twirk connection;
private final BufferedWriter writer;
private final OutputQueue queue;

private boolean isConnected = true;
private int MESSAGE_GAP_MILLIS = 1500; //We may not send more than 20 messages to the Twitch server / 30 seconds

private int messageGapMillis = 1500; //We may not send more than 20 messages to the Twitch server / 30 seconds

//***********************************************************************************************
// CONSTRUCTOR
//***********************************************************************************************
public OutputThread(Twirk connection, OutputQueue queue, BufferedReader reader, BufferedWriter writer){
this.connection = connection;
this.queue = queue;
this.writer = writer;

this.setName("Twirk-OutputThread");
}

@Override
public void run(){
String line;
Expand All @@ -48,74 +48,91 @@ public void run(){
else {
//If we get a null line from the queue, it might mean that the application interrupted the thread
// and wants us to shut down.
isConnected = connection.isConnected();
}
Thread.sleep(MESSAGE_GAP_MILLIS); }
catch (Exception ignored) {
isConnected = connection.isConnected();
}
Thread.sleep(messageGapMillis); }
catch (Exception ignored) {
/* Being interrupted probably means that we are about to shut down.
* If the socket is closed, it also means that we are about to shut down.
*
*
* If we are about to close down, isConnected will be set to false so we can just go back
* to the loop and automatically terminate from there. */
* to the loop and automatically terminate from there. */
}
}
}

//***********************************************************************************************
// PUBLIC
//***********************************************************************************************

/**Circumvents the message queue completely and attempts to send the message at once. Should only be used for sending
* PING responses.
*
*
* @param message
*/
public void quickSend(String message) {
try {
sendLine(message);
} catch (SocketException e) {
System.err.println("Could not QuickSend message. Socket was closed (OutputThread @ Twirk)");
}
}
}

/**Tells the thread to stop execution. Future messages written to the {@code OutputQueue} will
* not be sent by this {@code OutputThread} unless it is started again.
*/
public void end() {
isConnected = false;
this.queue.releaseWaitingThreads();
}


//***********************************************************************************************
// PACKAGE
//***********************************************************************************************

/**Tells the OutputThread how long we should wait, at minimum, in between
* each message sent to Twitch.
*
* @param millis the delay
*/
void setMessageDelay(int millis){
this.messageGapMillis = millis;
}

//***********************************************************************************************
// PRIVATE
//***********************************************************************************************

/**Sends a message AS IS, without modifying it in any way. Users of this method are responsible for
* formating the string correctly:
* formating 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{
if( !isConnected ){
System.err.println("Twirk is not connected! Sending messages will not succeed!");
}

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,
* 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,
* that would mean that we lose potential commands and such. Hence, instead we just drop
* that would mean that we lose potential commands and such. Hence, instead we just drop
* everything beyond the 510th character.
*/
if( message.length() > 510 ){
message = message.substring(0, 511);
}
}

try{
System.out.println("OUT " + message);
synchronized (writer) {
synchronized (writer) {
writer.write(message + "\r\n");
writer.flush();
}
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/gikk/twirk/SocketFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gikk.twirk;

import java.io.IOException;
import java.net.Socket;

/**
*
* @author Gikkman
*/
public interface SocketFactory {
public Socket createSocket() throws IOException;
}
Loading