$ git clone https://tcclient.ion.nu/tc_client.git
commit 503bb88ae09fc0bfbea8f806c086317d3a662b15
Author: Alicia <...>
Date: Fri Dec 11 19:52:41 2015 +0100
Adjusted for tinychat's protocol changes.
diff --git a/ChangeLog b/ChangeLog
index afb1e93..b99813b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
0.36:
Implemented /whois <nick/ID> to check someone's username.
Changed the /whois output to be more human-readable in cases where the user isn't logged in.
+Adjusted for tinychat's protocol changes.
modbot: take time offset into account for the duration of videos.
modbot: added support for /mbpa (pause) and /mbpl (play/resume)
modbot: check that videos can be embedded (for the flash client) before adding them to the queue.
diff --git a/Makefile b/Makefile
index 943e621..e946c34 100644
--- a/Makefile
+++ b/Makefile
@@ -61,6 +61,8 @@ INSTALLDEPS=tc_client
tc_client: $(OBJ)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
+# Make sure client.o gets rebuilt if we change the version number in the Makefile
+client.o: Makefile
utils: $(UTILS) tc_client
diff --git a/client.c b/client.c
index cff4be3..6371ad1 100644
--- a/client.c
+++ b/client.c
@@ -121,10 +121,10 @@ char* gethost(char *channel, char *password)
return host;
}
-char* getkey(const char* id, const char* channel)
+char* getkey(int id, const char* channel)
{
- char url[strlen("http://apl.tinychat.com/api/captcha/check.php?guest%5Fid=&room=tinychat%5E0")+strlen(id)+strlen(channel)];
- sprintf(url, "http://apl.tinychat.com/api/captcha/check.php?guest%%5Fid=%s&room=tinychat%%5E%s", id, channel);
+ char url[snprintf(0,0, "http://apl.tinychat.com/api/captcha/check.php?guest%%5Fid=%i&room=tinychat%%5E%s", id, channel)+1];
+ sprintf(url, "http://apl.tinychat.com/api/captcha/check.php?guest%%5Fid=%i&room=tinychat%%5E%s", id, channel);
char* response=http_get(url, 0);
char* key=strstr(response, "\"key\":\"");
@@ -428,13 +428,30 @@ int main(int argc, char** argv)
amfobjitem(&amf, "objectEncoding");
amfnum(&amf, 0);
amfobjend(&amf);
- amfstring(&amf, channel);
- amfstring(&amf, modkey?modkey:"none");
- amfstring(&amf, "default"); // This item is called roomtype in the same HTTP response that gives us the server (IP+port) to connect to, but "default" seems to work fine too.
- amfstring(&amf, "tinychat");
- amfstring(&amf, account_user?account_user:"");
- amfstring(&amf, "");
- amfstring(&amf, cookie);
+ amfobjstart(&amf);
+ amfobjitem(&amf, "version");
+ amfstring(&amf, "Desktop");
+
+ amfobjitem(&amf, "type");
+ amfstring(&amf, "default"); // This item is called roomtype in the same HTTP response that gives us the server (IP+port) to connect to, but "default" seems to work fine too.
+
+ amfobjitem(&amf, "account");
+ amfstring(&amf, account_user?account_user:"");
+
+ amfobjitem(&amf, "prefix");
+ amfstring(&amf, "tinychat");
+
+ amfobjitem(&amf, "room");
+ amfstring(&amf, channel);
+
+ if(modkey)
+ {
+ amfobjitem(&amf, "autoop");
+ amfstring(&amf, modkey);
+ }
+ amfobjitem(&amf, "cookie");
+ amfstring(&amf, cookie);
+ amfobjend(&amf);
amfsend(&amf, sock);
free(modkey);
free(cookie);
@@ -675,17 +692,16 @@ int main(int argc, char** argv)
amfsend(&amf, sock);
continue;
}
- else if(!strncmp(buf, "/whois ", 7)) // Request username
+ else if(!strncmp(buf, "/whois ", 7)) // Get account username
{
- amfinit(&amf, 3);
- amfstring(&amf, "account");
- amfnum(&amf, 0);
- amfnull(&amf);
- int id=idlist_get(&buf[7]);
- if(id<0 && isdigit(buf[7])){id=atoi(&buf[7]);}
- sprintf(buf, "%i", id);
- amfstring(&amf, buf);
- amfsend(&amf, sock);
+ const char* account=idlist_getaccount(&buf[7]);
+ if(account)
+ {
+ printf("%s is logged in as %s\n", &buf[7], account);
+ }else{
+ printf("%s is not logged in\n", &buf[7]);
+ }
+ fflush(stdout);
continue;
}
}
@@ -732,32 +748,8 @@ int main(int argc, char** argv)
if(!strcmp(amfin->items[0].string.string, "_error"))
printamf(amfin);
}
- if(amfin->itemcount>0 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "registered") && amfin->items[amfin->itemcount-1].type==AMF_STRING)
- {
- char* id=amfin->items[amfin->itemcount-1].string.string;
- printf("Connection ID: %s\n", id);
- char* key=getkey(id, channel);
- curl_easy_cleanup(curl); // At this point we should be done with HTTP requests
- curl=0;
- if(!key){printf("Failed to get channel key\n"); return 1;}
-
- amfinit(&amf, 3);
- amfstring(&amf, "cauth");
- amfnum(&amf, 0);
- amfnull(&amf); // Means nothing but is apparently critically important for cauth at least
- amfstring(&amf, key);
- amfsend(&amf, sock);
- free(key);
-
- amfinit(&amf, 3);
- amfstring(&amf, "nick");
- amfnum(&amf, 0);
- amfnull(&amf);
- amfstring(&amf, nickname);
- amfsend(&amf, sock);
- }
// Items for privmsg: 0=cmd, 2=channel, 3=msg, 4=color/lang, 5=nick
- else if(amfin->itemcount>5 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "privmsg") && amfin->items[3].type==AMF_STRING && amfin->items[4].type==AMF_STRING && amfin->items[5].type==AMF_STRING)
+ if(amfin->itemcount>5 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "privmsg") && amfin->items[3].type==AMF_STRING && amfin->items[4].type==AMF_STRING && amfin->items[5].type==AMF_STRING)
{
size_t len;
char* msg=fromnumlist(amfin->items[3].string.string, &len);
@@ -812,27 +804,68 @@ int main(int argc, char** argv)
fflush(stdout);
}
// users on channel entry. there's also a "joinsdone" command for some reason...
- else if(amfin->itemcount>3 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "joins"))
+ else if(amfin->itemcount>2 && amfin->items[0].type==AMF_STRING && (amf_comparestrings_c(&amfin->items[0].string, "joins") || amf_comparestrings_c(&amfin->items[0].string, "join") || amf_comparestrings_c(&amfin->items[0].string, "registered")))
{
- printf("Currently online: ");
+ if(amf_comparestrings_c(&amfin->items[0].string, "joins"))
+ {
+ printf("Currently online: ");
+ }else{
+ printf("%s ", timestamp());
+ }
int i;
- for(i = 3; i < amfin->itemcount-1; i+=2)
+ for(i = 2; i < amfin->itemcount; ++i)
{
- // a "numeric" id precedes each nick, i.e. i is the id, i+1 is the nick
- if(amfin->items[i].type==AMF_STRING && amfin->items[i+1].type==AMF_STRING)
+ if(amfin->items[i].type==AMF_OBJECT)
{
- idlist_add(atoi(amfin->items[i].string.string), amfin->items[i+1].string.string);
- printf("%s%s", (i==3?"":", "), amfin->items[i+1].string.string);
+ struct amfitem* item=amf_getobjmember(&amfin->items[i].object, "id");
+ if(item->type!=AMF_NUMBER){continue;}
+ int id=item->number;
+ item=amf_getobjmember(&amfin->items[i].object, "nick");
+ if(item->type!=AMF_STRING){continue;}
+ const char* nick=item->string.string;
+ item=amf_getobjmember(&amfin->items[i].object, "account");
+ if(item->type!=AMF_STRING){continue;}
+ const char* account=(item->string.string[0]?item->string.string:0);
+ item=amf_getobjmember(&amfin->items[i].object, "mod");
+ if(item->type!=AMF_BOOL){continue;}
+ char mod=item->boolean;
+ idlist_add(id, nick, account, mod);
+ printf("%s%s", (i==2?"":", "), nick);
+ if(amf_comparestrings_c(&amfin->items[0].string, "registered"))
+ {
+ char* key=getkey(id, channel);
+ curl_easy_cleanup(curl); // At this point we should be done with HTTP requests
+ curl=0;
+ if(!key){printf("Failed to get channel key\n"); return 1;}
+ amfinit(&amf, 3);
+ amfstring(&amf, "cauth");
+ amfnum(&amf, 0);
+ amfnull(&amf); // Means nothing but is apparently critically important for cauth at least
+ amfstring(&amf, key);
+ amfsend(&amf, sock);
+ free(key);
+ if(nickname[0]) // Empty = don't set one
+ {
+ amfinit(&amf, 3);
+ amfstring(&amf, "nick");
+ amfnum(&amf, 0);
+ amfnull(&amf);
+ amfstring(&amf, nickname);
+ amfsend(&amf, sock);
+ }
+ }
+ }
+ }
+ if(amf_comparestrings_c(&amfin->items[0].string, "joins"))
+ {
+ printf("\n");
+ }else{
+ printf(" entered the channel\n");
+ if(amf_comparestrings_c(&amfin->items[0].string, "registered"))
+ {
+ printf("Connection ID: %i\n", idlist[0].id);
}
}
- printf("\n");
- fflush(stdout);
- }
- // join ("join", 0, "<ID>", "guest-<ID>")
- else if(amfin->itemcount==4 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "join") && amfin->items[2].type==AMF_NUMBER && amfin->items[3].type==AMF_STRING)
- {
- idlist_add(amfin->items[2].number, amfin->items[3].string.string);
- printf("%s %s entered the channel\n", timestamp(), amfin->items[3].string.string);
fflush(stdout);
}
// quit/part
@@ -899,20 +932,6 @@ int main(int argc, char** argv)
fflush(stdout);
}
}
- // oper, identifies mods
- else if(amfin->itemcount==4 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "oper") && amfin->items[3].type==AMF_STRING)
- {
- idlist_set_op(amfin->items[3].string.string, 1);
- printf("%s is a moderator.\n", amfin->items[3].string.string);
- fflush(stdout);
- }
- // deop, removes moderator privilege
- else if(amfin->itemcount==4 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "deop") && amfin->items[3].type==AMF_STRING)
- {
- idlist_set_op(amfin->items[3].string.string, 0);
- printf("%s is no longer a moderator.\n", amfin->items[3].string.string);
- fflush(stdout);
- }
// nickinuse, the nick we wanted to change to is already taken
else if(amfin->itemcount>0 && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, "nickinuse"))
{
diff --git a/idlist.c b/idlist.c
index 8cc0e16..01a9901 100644
--- a/idlist.c
+++ b/idlist.c
@@ -1,6 +1,6 @@
/*
tc_client, a simple non-flash client for tinychat(.com)
- Copyright (C) 2014 alicia@ion.nu
+ Copyright (C) 2014-2015 alicia@ion.nu
Copyright (C) 2014-2015 Jade Lea
This program is free software: you can redistribute it and/or modify
@@ -22,13 +22,15 @@
struct idmap* idlist=0;
int idlistlen=0;
-void idlist_add(int id, const char* name)
+void idlist_add(int id, const char* name, const char* account, char op)
{
idlist_remove(name);
++idlistlen;
idlist=realloc(idlist, sizeof(struct idmap)*idlistlen);
idlist[idlistlen-1].id=id;
+ idlist[idlistlen-1].op=op;
idlist[idlistlen-1].name=strdup(name);
+ idlist[idlistlen-1].account=(account?strdup(account):0);
}
void idlist_remove(const char* name)
@@ -75,17 +77,19 @@ int idlist_get(const char* name)
return -1;
}
-void idlist_set_op(const char* name, char op)
+const char* idlist_getaccount(const char* name)
{
+ int len;
+ for(len=0; name[len]&&name[len]!=' '; ++len);
int i;
for(i=0; i<idlistlen; ++i)
{
- if(!strcmp(name, idlist[i].name))
+ if(!strncmp(name, idlist[i].name, len) && !idlist[i].name[len])
{
- idlist[i].op=op;
- return;
+ return idlist[i].account;
}
}
+ return 0;
}
char idlist_is_op(const char* name)
diff --git a/idlist.h b/idlist.h
index b7c7f81..27ef08e 100644
--- a/idlist.h
+++ b/idlist.h
@@ -1,6 +1,6 @@
/*
tc_client, a simple non-flash client for tinychat(.com)
- Copyright (C) 2014 alicia@ion.nu
+ Copyright (C) 2014-2015 alicia@ion.nu
Copyright (C) 2014-2015 Jade Lea
This program is free software: you can redistribute it and/or modify
@@ -20,14 +20,15 @@ struct idmap
const char* name;
int id;
char op;
+ const char* account;
};
extern struct idmap* idlist;
extern int idlistlen;
-extern void idlist_add(int id, const char* name);
+extern void idlist_add(int id, const char* name, const char* account, char op);
extern void idlist_remove(const char* name);
extern void idlist_rename(const char* oldname, const char* newname);
extern int idlist_get(const char* name);
-extern void idlist_set_op(const char* name, char op);
+extern const char* idlist_getaccount(const char* name);
extern char idlist_is_op(const char* name);