Files
Reminder-XMPP/reminder.c
2026-01-15 20:35:50 +00:00

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;
}