diff --git a/plugapi/plugapi-tests.ts b/plugapi/plugapi-tests.ts
new file mode 100644
index 0000000000..279a5b659d
--- /dev/null
+++ b/plugapi/plugapi-tests.ts
@@ -0,0 +1,35 @@
+///
+
+import * as PlugAPI from "plugapi";
+
+new PlugAPI({
+ email: "",
+ password: ""
+}, function (err, bot) {
+ if (!err) {
+ const ROOM = "roomslug";
+ bot.connect(ROOM); // The part after https://plug.dj
+
+ bot.on(PlugAPI.events.ROOM_JOIN, function (room) {
+ console.log("Joined " + room);
+ });
+
+ bot.on("chat", function (data) {
+ if (data.type == "emote") {
+ console.log(data.from + data.message);
+ } else {
+ console.log(data.from + "> " + data.message);
+ }
+ });
+
+ bot.on("error", function () {
+ bot.connect(ROOM);
+ });
+
+ bot.deleteAllChat = false;
+ bot.multiLine = true;
+ bot.multiLineLimit = 5;
+ } else {
+ console.log("Error initializing plugAPI: " + err);
+ }
+});
\ No newline at end of file
diff --git a/plugapi/plugapi.d.ts b/plugapi/plugapi.d.ts
new file mode 100644
index 0000000000..90351ac53a
--- /dev/null
+++ b/plugapi/plugapi.d.ts
@@ -0,0 +1,420 @@
+// Type definitions for plugapi
+// Project: https://www.npmjs.com/package/plugapi
+// Definitions by: Brice Theurillat
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+
+declare module "plugapi" {
+ interface PlugLogin {
+ email: string;
+ password: string;
+ }
+
+ interface Notification {
+ action: string;
+ id: number;
+ timestamp: string;
+ value: string;
+ }
+
+ interface RawChatMessage {
+ cid: string;
+ message: string;
+ sub: number;
+ uid: number;
+ un: string;
+ }
+
+ interface Media {
+ author: string;
+ format: number;
+ image: string;
+ cid: string;
+ duration: number;
+ title: string;
+ id: number;
+ }
+
+ interface Score {
+ positive: number;
+ listeners: number;
+ grabs: number;
+ negative: number;
+ skipped: number;
+ }
+
+ interface LastPlay {
+ dj: User.DJ;
+ media: Media;
+ score: Score;
+ }
+
+ interface FollowJoinData {
+ r: number;
+ un: string;
+ id: string;
+ }
+
+ interface LogObject {
+ log: () => void;
+ }
+
+ namespace User {
+ interface Default {
+ username: string;
+ language: string;
+ avatarID: string;
+ }
+
+ interface Extended extends Default {
+ status: number;
+ fans: number;
+ listenerPoints: number;
+ id: string;
+ curatorPoints: number;
+ djPoints: number;
+ }
+
+ interface Update extends Extended {
+ dateJoined: string;
+ }
+
+ interface Room extends Default {
+ sub: number;
+ level: number;
+ joined: string;
+ id: number;
+ badge: string;
+ role: number;
+ gRole: number;
+ slug: string;
+ }
+
+ interface User extends Room {
+ silver: boolean;
+ guest: boolean;
+ }
+
+ interface DJ extends Room {
+ blurp: any;
+ grab: boolean;
+ status: number;
+ vote: number;
+ }
+
+ interface Audience extends DJ {
+ ignores: any[];
+ notifications: Notification[];
+ pp: number;
+ pw: number;
+ xp: number;
+ }
+ }
+
+ namespace Enum {
+ interface RoomRole {
+ NONE: number;
+ RESIDENTDJ: number;
+ BOUNCER: number;
+ MANAGER: number;
+ COHOST: number;
+ HOST: number;
+ }
+
+ interface GlobalRole {
+ NONE: number;
+ VOLUNTEER: number;
+ AMBASSADOR: number;
+ LEADER: number;
+ ADMIN: number;
+ }
+
+ interface Status {
+ OFFLINE: number;
+ ONLINE: number;
+ }
+
+ interface Ban {
+ HOUR: "h";
+ DAY: "d";
+ PERMA: "f";
+ }
+
+ interface BanReason {
+ SPAMMING_TROLLING: number;
+ VERBAL_ABUSE: number;
+ OFFENSIVE_MEDIA: number;
+ INAPPROPRIATE_GENRE: number;
+ NEGATIVE_ATTITUDE: number;
+ }
+
+ interface Mute {
+ SHORT: "s";
+ MEDIUM: "m";
+ LONG: "l";
+ }
+
+ interface MuteReason {
+ VIOLATING_COMMUNITY_RULES: number;
+ VERBAL_ABUSE: number;
+ SPAMMING_TROLLING: number;
+ OFFENSIVE_LANGUAGE: number;
+ NEGATIVE_ATTITUDE: number;
+ }
+
+ interface Events {
+ ADVANCE: "advance";
+ BAN: "ban";
+ BOOTH_LOCKED: "boothLocked";
+ CHAT: "chat";
+ CHAT_COMMAND: "command";
+ CHAT_DELETE: "chatDelete";
+ CHAT_LEVEL_UPDATE: "roomMinChatLevelUpdate";
+ COMMAND: "command";
+ DJ_LIST_CYCLE: "djListCycle";
+ DJ_LIST_UPDATE: "djListUpdate";
+ DJ_LIST_LOCKED: "djListLocked";
+ EARN: "earn";
+ FOLLOW_JOIN: "followJoin";
+ FLOOD_CHAT: "floodChat";
+ FRIEND_REQUEST: "friendRequest";
+ GIFTED: "gifted";
+ GRAB: "grab";
+ KILL_SESSION: "killSession";
+ MAINT_MODE: "plugMaintenance";
+ MAINT_MODE_ALERT: "plugMaintenanceAlert";
+ MODERATE_ADD_DJ: "modAddDJ";
+ MODERATE_ADD_WAITLIST: "modAddWaitList";
+ MODERATE_AMBASSADOR: "modAmbassador";
+ MODERATE_BAN: "modBan";
+ MODERATE_MOVE_DJ: "modMoveDJ";
+ MODERATE_MUTE: "modMute";
+ MODERATE_REMOVE_DJ: "modRemoveDJ";
+ MODERATE_REMOVE_WAITLIST: "modRemoveWaitList";
+ MODERATE_SKIP: "modSkip";
+ MODERATE_STAFF: "modStaff";
+ NOTIFY: "notify";
+ PDJ_MESSAGE: "pdjMessage";
+ PDJ_UPDATE: "pdjUpdate";
+ PING: "ping";
+ PLAYLIST_CYCLE: "playlistCycle";
+ REQUEST_DURATION: "requestDuration";
+ REQUEST_DURATION_RETRY: "requestDurationRetry";
+ ROOM_CHANGE: "roomChanged";
+ ROOM_DESCRIPTION_UPDATE: "roomDescriptionUpdate";
+ ROOM_JOIN: "roomJoin";
+ ROOM_NAME_UPDATE: "roomNameUpdate";
+ ROOM_VOTE_SKIP: "roomVoteSkip";
+ ROOM_WELCOME_UPDATE: "roomWelcomeUpdate";
+ SESSION_CLOSE: "sessionClose";
+ SKIP: "skip";
+ STROBE_TOGGLE: "strobeToggle";
+ USER_COUNTER_UPDATE: "userCounterUpdate";
+ USER_FOLLOW: "userFollow";
+ USER_JOIN: "userJoin";
+ USER_LEAVE: "userLeave";
+ USER_UPDATE: "userUpdate";
+ VOTE: "vote";
+ }
+ }
+
+ namespace Event {
+ interface BoothCycle {
+ moderator: string;
+ cycle: boolean;
+ }
+
+ interface BoothLocked {
+ m: string;
+ c: boolean;
+ ml: string;
+ f: boolean;
+ }
+
+ interface Chat {
+ raw: RawChatMessage;
+ id: string;
+ from: User.User;
+ message: string;
+ mentions: Array;
+ muted: boolean;
+ type: string;
+ }
+
+ interface ChatDelete {
+ mi: number;
+ chatID: string;
+ }
+
+ type Grab = number;
+
+ interface Advance {
+ media: Media;
+ startTime: string;
+ historyID: string;
+ djs: User.DJ[];
+ currentDJ: User.DJ;
+ playlistID: number;
+ lastPlay: LastPlay;
+ }
+
+ interface DJListUpdate {
+ djs: User.DJ[];
+ remove: string;
+ }
+
+ interface Emote {
+ fromID: string;
+ message: string;
+ from: string;
+ type: string;
+ chatID: string;
+ }
+
+ interface FollowJoin {
+ data: FollowJoinData;
+ type: string;
+ }
+
+ interface ModAddDJ {
+ moderator: string;
+ username: string;
+ }
+
+ interface ModBan {
+ moderator: string;
+ username: string;
+ duration: number;
+ ref: string;
+ reason: string;
+ }
+
+ interface ModMoveDJ {
+ moderator: string;
+ index: number;
+ old: number;
+ userID: string;
+ }
+
+ interface ModRemoveDJ {
+ moderator: string;
+ username: string;
+ userID: string;
+ }
+
+ interface ModSkip {
+ mi: number;
+ m: string;
+ }
+
+ interface RoomMinChatLevelUpdate {
+ level: number;
+ id: number;
+ user: User.User;
+ }
+
+ type RoomJoin = string;
+
+ type UserJoin = User.User;
+
+ type UserLeave = User.User;
+
+ interface UserUpdate {
+ username: string;
+ status: number;
+ fans: number;
+ listenerPoints: number;
+ dateJoined: string;
+ language: string;
+ avatarID: string;
+ id: string;
+ curatorPoints: number;
+ djPoints: number;
+ }
+
+ interface Vote {
+ i: number;
+ v: number;
+ }
+
+ interface Command extends Event.Chat {
+ command: string;
+ args: string[];
+ respond: Function;
+ respondTimeout: Function;
+ havePermission: Function;
+ isFrom: Function;
+ }
+ }
+
+ interface PlugAPIInstance {
+ deleteAllChat: boolean;
+ multiLine: boolean;
+ multiLineLimit: number;
+
+ connect(room: string): void;
+ changeDJCycle(enabled: boolean, callback?: () => void): boolean;
+ changeRoom(room: string, callback?: () => void): void;
+ close(): void;
+ getAdmins(): User.Extended[];
+ getAmbassadors(): User.Extended[];
+ getAudience(): User.Audience[];
+ getDJ(): User.DJ;
+ getDJs(): User.DJ[];
+ getHost(): User.Extended;
+ getMedia(): Media;
+ getRoomScore(): Score;
+ getSelf(): User.Audience;
+ getStaff(): User.Extended[];
+ getTimeElapsed(): number;
+ getTimeRemaining(): number;
+ getUser(userID: number): User.DJ;
+ getUsers(): User.DJ[];
+ getWaitList(): User.Extended;
+ getWaitListPosition(userID: number): number;
+ havePermission(userID: number, permission: number, global?: boolean): boolean;
+ joinBooth(callback?: () => void): boolean;
+ leaveBooth(callback?: () => void): boolean;
+ selfSkip(callback?: () => void): boolean;
+ sendChat(msg: string, timeout?: number): void;
+ setLogger(logObject: LogObject): boolean;
+
+ on(event: "boothCycle", callback: (data: Event.BoothCycle) => void): void;
+ on(event: "boothLocked", callback: (data: Event.BoothLocked) => void): void;
+ on(event: "chat", callback: (data: Event.Chat) => void): void;
+ on(event: "chatDelete", callback: (data: Event.ChatDelete) => void): void;
+ on(event: "grab", callback: (data: Event.Grab) => void): void;
+ on(event: "advance", callback: (data: Event.Advance) => void): void;
+ on(event: "djListUpdate", callback: (data: Event.DJListUpdate) => void): void;
+ on(event: "emote", callback: (data: Event.Emote) => void): void;
+ on(event: "followJoin", callback: (data: Event.FollowJoin) => void): void;
+ on(event: "modAddDJ", callback: (data: Event.ModAddDJ) => void): void;
+ on(event: "modBan", callback: (data: Event.ModBan) => void): void;
+ on(event: "modMoveDJ", callback: (data: Event.ModMoveDJ) => void): void;
+ on(event: "modRemoveDJ", callback: (data: Event.ModRemoveDJ) => void): void;
+ on(event: "modSkip", callback: (data: Event.ModSkip) => void): void;
+ on(event: "roomMinChatLevelUpdate", callback: (data: Event.RoomMinChatLevelUpdate) => void): void;
+ on(event: "roomJoin", callback: (data: Event.RoomJoin) => void): void;
+ on(event: "userJoin", callback: (data: Event.UserJoin) => void): void;
+ on(event: "userLeave", callback: (data: Event.UserLeave) => void): void;
+ on(event: "userUpdate", callback: (data: Event.UserUpdate) => void): void;
+ on(event: "vote", callback: (data: Event.Vote) => void): void;
+ on(event: "command", callback: (data: Event.Command) => void): void;
+ on(event: string, callback: (data: any) => void): void;
+ }
+
+ interface PlugAPIStatic {
+ new (login: PlugLogin, callback?: (error: Error, bot: PlugAPIInstance) => void): PlugAPIInstance;
+ new (login: PlugLogin, callback?: (bot: PlugAPIInstance) => void): PlugAPIInstance;
+ ROOM_ROLE: Enum.RoomRole;
+ GLOBAL_ROLES: Enum.GlobalRole;
+ STATUS: Enum.Status;
+ BAN: Enum.Ban;
+ BAN_REASON: Enum.BanReason;
+ MUTE: Enum.Mute;
+ MUTE_REASON: Enum.MuteReason;
+ events: Enum.Events;
+ }
+
+ var PlugAPI: PlugAPIStatic;
+ export = PlugAPI;
+}
\ No newline at end of file