182 lines
6.6 KiB
C
182 lines
6.6 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <stdint.h>
|
|
#include <strophe.h>
|
|
|
|
#define F "r.db"
|
|
#define J "reminder@example.org"
|
|
#define P "safak"
|
|
#define R "remiders@room.example.org"
|
|
#define N "ReminderBot"
|
|
|
|
typedef struct E{long long i;char*r,*u,*m;time_t d;struct E*n;}E;
|
|
typedef struct{xmpp_ctx_t*c;xmpp_conn_t*o;E*l;long long k;}B;
|
|
|
|
static void T(xmpp_ctx_t*c,xmpp_stanza_t*p,const char*t){
|
|
xmpp_stanza_t*n=xmpp_stanza_new(c);
|
|
xmpp_stanza_set_text(n,t);
|
|
xmpp_stanza_add_child(p,n);
|
|
xmpp_stanza_release(n);
|
|
}
|
|
|
|
static time_t pt(const char*s){
|
|
time_t w=time(0);struct tm t=*localtime(&w);int h,m;
|
|
if(strchr(s,':')&&sscanf(s,"%d:%d",&h,&m)==2){
|
|
struct tm f=t;f.tm_hour=h;f.tm_min=m;f.tm_sec=0;
|
|
time_t x=mktime(&f);return x<=w?x+86400:x;
|
|
}
|
|
char*e;long v=strtol(s,&e,10);if(e==s)return 0;
|
|
char a=tolower(*e),b=e[1]?tolower(e[1]):0;
|
|
if(a=='s')return w+v;
|
|
if(a=='m'&&b!='o')return w+v*60;
|
|
if(a=='h')return w+v*3600;
|
|
if(a=='d')return w+v*86400;
|
|
if(a=='w')return w+v*604800;
|
|
if(a=='m'&&b=='o'){int n=t.tm_mon+v;t.tm_year+=n/12;t.tm_mon=n%12;if(t.tm_mon<0){t.tm_mon+=12;t.tm_year--;}return mktime(&t);}
|
|
if(a=='y'){int n=t.tm_mon+v*12;t.tm_year+=n/12;t.tm_mon=n%12;return mktime(&t);}
|
|
return 0;
|
|
}
|
|
|
|
static void sv(B*b){
|
|
FILE*f=fopen(F,"wb");if(!f)return;
|
|
int64_t c=b->k;fwrite(&c,8,1,f);
|
|
for(E*r=b->l;r;r=r->n){
|
|
int64_t i=r->i,d=r->d;
|
|
uint16_t a=strlen(r->r),e=strlen(r->u),g=strlen(r->m);
|
|
fwrite(&i,8,1,f);fwrite(&d,8,1,f);
|
|
fwrite(&a,2,1,f);fwrite(&e,2,1,f);fwrite(&g,2,1,f);
|
|
fwrite(r->r,1,a,f);fwrite(r->u,1,e,f);fwrite(r->m,1,g,f);
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
static void ld(B*b){
|
|
FILE*f=fopen(F,"rb");if(!f)return;
|
|
int64_t c;if(fread(&c,8,1,f)!=1){fclose(f);return;}
|
|
b->k=c;int64_t i;
|
|
while(fread(&i,8,1,f)==1){
|
|
int64_t d;uint16_t a,e,g;
|
|
if(fread(&d,8,1,f)!=1||fread(&a,2,1,f)!=1||fread(&e,2,1,f)!=1||fread(&g,2,1,f)!=1)break;
|
|
E*r=malloc(sizeof(E));r->i=i;r->d=d;
|
|
r->r=malloc(a+1);r->u=malloc(e+1);r->m=malloc(g+1);
|
|
if(fread(r->r,1,a,f)!=a||fread(r->u,1,e,f)!=e||fread(r->m,1,g,f)!=g){free(r->r);free(r->u);free(r->m);free(r);break;}
|
|
r->r[a]=r->u[e]=r->m[g]=0;r->n=b->l;b->l=r;
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
static void rp(B*b,const char*t,const char*y,const char*m){
|
|
xmpp_stanza_t*s=xmpp_stanza_new(b->c);
|
|
xmpp_stanza_set_name(s,"message");
|
|
xmpp_stanza_set_type(s,y);
|
|
xmpp_stanza_set_attribute(s,"to",t);
|
|
xmpp_stanza_t*o=xmpp_stanza_new(b->c);
|
|
xmpp_stanza_set_name(o,"body");
|
|
T(b->c,o,m);
|
|
xmpp_stanza_add_child(s,o);
|
|
xmpp_stanza_release(o);
|
|
xmpp_send(b->o,s);
|
|
xmpp_stanza_release(s);
|
|
}
|
|
|
|
static int tc(xmpp_conn_t*o,void*x){
|
|
B*b=x;time_t w=time(0);E**p=&b->l;int z=0;
|
|
while(*p){
|
|
E*r=*p;
|
|
if(w>=r->d){
|
|
char q[2048];snprintf(q,2048,"%s: %s",r->u,r->m);
|
|
rp(b,r->r,strstr(r->r,"@conf")||strstr(r->r,"@muc")?"groupchat":"chat",q);
|
|
*p=r->n;free(r->r);free(r->u);free(r->m);free(r);z=1;
|
|
}else p=&r->n;
|
|
}
|
|
if(z)sv(b);return 1;
|
|
}
|
|
|
|
static int mc(xmpp_conn_t*o,xmpp_stanza_t*s,void*x){
|
|
B*b=x;
|
|
xmpp_stanza_t*y=xmpp_stanza_get_child_by_name(s,"body");
|
|
if(!y)return 1;
|
|
char*t=xmpp_stanza_get_text(y);
|
|
if(!t||*t!='!'){if(t)xmpp_free(b->c,t);return 1;}
|
|
const char*f=xmpp_stanza_get_attribute(s,"from");
|
|
const char*p=xmpp_stanza_get_attribute(s,"type");
|
|
if(!p)p="chat";
|
|
if(!strcmp(p,"groupchat")){const char*l=strchr(f,'/');if(l&&!strcmp(l+1,N)){xmpp_free(b->c,t);return 1;}}
|
|
char*e=strdup(t),*c=strtok(e," "),rm[512],us[256];
|
|
const char*l=strchr(f,'/');
|
|
if(l){size_t n=l-f;memcpy(rm,f,n);rm[n]=0;strncpy(us,l+1,255);us[255]=0;}
|
|
else{strncpy(rm,f,511);strncpy(us,f,255);}
|
|
if(c&&!strcmp(c,"!remind")){
|
|
char*ts=strtok(0," "),*mg=strtok(0,"");
|
|
if(ts&&mg){
|
|
time_t d=pt(ts);
|
|
if(d>0){
|
|
E*r=malloc(sizeof(E));r->i=++b->k;r->r=strdup(rm);r->u=strdup(us);r->m=strdup(mg);r->d=d;r->n=b->l;b->l=r;sv(b);
|
|
struct tm*z=localtime(&d);char q[256];
|
|
snprintf(q,256,"Saved [%lld]: %04d-%02d-%02d %02d:%02d:%02d",r->i,z->tm_year+1900,z->tm_mon+1,z->tm_mday,z->tm_hour,z->tm_min,z->tm_sec);
|
|
rp(b,rm,p,q);
|
|
}else rp(b,rm,p,"Invalid format. Try: 30s, 5m, 1h, 10:30");
|
|
}else rp(b,rm,p,"Usage: !remind <time> <msg>");
|
|
}else if(c&&!strcmp(c,"!list")){
|
|
char q[4096]="";int n=0;
|
|
for(E*r=b->l;r&&n<10;r=r->n)if(!strcmp(r->r,rm)){
|
|
struct tm*z=localtime(&r->d);char v[256];
|
|
snprintf(v,256,"[%lld] %02d:%02d (%s): %s\n",r->i,z->tm_hour,z->tm_min,r->u,r->m);
|
|
strncat(q,v,4095-strlen(q));n++;
|
|
}
|
|
rp(b,rm,p,*q?q:"No reminders.");
|
|
}else if(c&&!strcmp(c,"!del")){
|
|
char*i=strtok(0," ");
|
|
if(i){
|
|
long long d=atoll(i);E**ptr=&b->l;int z=0;
|
|
while(*ptr){if((*ptr)->i==d){E*t=*ptr;*ptr=t->n;free(t->r);free(t->u);free(t->m);free(t);z=1;break;}ptr=&(*ptr)->n;}
|
|
if(z){sv(b);char q[64];snprintf(q,64,"Deleted %lld",d);rp(b,rm,p,q);}
|
|
else rp(b,rm,p,"Not found.");
|
|
}
|
|
}
|
|
free(e);xmpp_free(b->c,t);return 1;
|
|
}
|
|
|
|
static void cc(xmpp_conn_t*o,xmpp_conn_event_t st,int e,xmpp_stream_error_t*r,void*x){
|
|
B*b=x;
|
|
if(st==XMPP_CONN_CONNECT){
|
|
xmpp_handler_add(o,mc,0,"message",0,b);
|
|
xmpp_stanza_t*p=xmpp_stanza_new(b->c);
|
|
xmpp_stanza_set_name(p,"presence");
|
|
xmpp_send(o,p);xmpp_stanza_release(p);
|
|
char t[512];snprintf(t,512,"%s/%s",R,N);
|
|
p=xmpp_stanza_new(b->c);
|
|
xmpp_stanza_set_name(p,"presence");
|
|
xmpp_stanza_set_attribute(p,"to",t);
|
|
xmpp_stanza_t*y=xmpp_stanza_new(b->c);
|
|
xmpp_stanza_set_name(y,"x");
|
|
xmpp_stanza_set_ns(y,"http://jabber.org/protocol/muc");
|
|
xmpp_stanza_t*h=xmpp_stanza_new(b->c);
|
|
xmpp_stanza_set_name(h,"history");
|
|
xmpp_stanza_set_attribute(h,"maxstanzas","0");
|
|
xmpp_stanza_add_child(y,h);xmpp_stanza_release(h);
|
|
xmpp_stanza_add_child(p,y);xmpp_stanza_release(y);
|
|
xmpp_send(o,p);xmpp_stanza_release(p);
|
|
xmpp_timed_handler_add(o,tc,5000,b);
|
|
}else xmpp_stop(b->c);
|
|
}
|
|
|
|
int main(void){
|
|
B b={0};ld(&b);xmpp_initialize();
|
|
b.c=xmpp_ctx_new(0,xmpp_get_default_logger(XMPP_LEVEL_ERROR));
|
|
b.o=xmpp_conn_new(b.c);
|
|
xmpp_conn_set_jid(b.o,J);
|
|
xmpp_conn_set_pass(b.o,P);
|
|
xmpp_conn_set_flags(b.o,XMPP_CONN_FLAG_MANDATORY_TLS);
|
|
xmpp_connect_client(b.o,0,0,cc,&b);
|
|
xmpp_run(b.c);
|
|
xmpp_conn_release(b.o);
|
|
xmpp_ctx_free(b.c);
|
|
xmpp_shutdown();
|
|
return 0;
|
|
}
|
|
|