Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update New Tg #6

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Update New Tg #6

wants to merge 1 commit into from

Conversation

hsamdew
Copy link

@hsamdew hsamdew commented Apr 28, 2017

/*
This file is part of telegram-cli.

Telegram-cli is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

Telegram-cli is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this telegram-cli.  If not, see <http://www.gnu.org/licenses/>.

Copyright Vitaly Valtman 2013-2015

*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef USE_LUA
#include "lua-tg.h"

#include <string.h>
#include <stdlib.h>

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#ifdef EVENT_V2
#include <event2/event.h>
#else
#include <event.h>
#include "event-old.h"
#endif
lua_State *luaState;

//#include "interface.h"
//#include "auto/constants.h"
#include <tgl/tgl.h>
#include <tgl/tgl-queries.h>
#include "interface.h"

#include <assert.h>
extern int verbosity;
extern struct tgl_state *TLS;

static int have_file;

void print_start (void);
void print_end (void);

int ps_lua_pcall (lua_State *l, int a, int b, int c) {
print_start ();
int r = lua_pcall (l, a, b, c);
print_end ();
return r;
}

#define my_lua_checkstack(L,x) assert (lua_checkstack (L, x))
void push_user (tgl_peer_t *P);
void push_peer (tgl_peer_id_t id, tgl_peer_t *P);

void lua_add_string_field (const char *name, const char *value) {
assert (name && strlen (name));
if (!value || !strlen (value)) { return; }
my_lua_checkstack (luaState, 3);
lua_pushstring (luaState, name);
lua_pushstring (luaState, value);
lua_settable (luaState, -3);
}

void lua_add_lstring_field (const char *name, const char *value, int len) {
assert (name && strlen (name));
if (!value || !len) { return; }
my_lua_checkstack (luaState, 3);
lua_pushstring (luaState, name);
lua_pushlstring (luaState, value, len);
lua_settable (luaState, -3);
}

void lua_add_string_field_arr (int num, const char *value) {
if (!value || !strlen (value)) { return; }
my_lua_checkstack (luaState, 3);
lua_pushnumber (luaState, num);
lua_pushstring (luaState, value);
lua_settable (luaState, -3);
}

void lua_add_num_field (const char *name, double value) {
assert (name && strlen (name));
my_lua_checkstack (luaState, 3);
lua_pushstring (luaState, name);
lua_pushnumber (luaState, value);
lua_settable (luaState, -3);
}

void push_tgl_peer_type (int x) {
switch (x) {
case TGL_PEER_USER:
lua_pushstring (luaState, "user");
break;
case TGL_PEER_CHAT:
lua_pushstring (luaState, "chat");
break;
case TGL_PEER_ENCR_CHAT:
lua_pushstring (luaState, "encr_chat");
break;
case TGL_PEER_CHANNEL:
lua_pushstring (luaState, "channel");
break;
default:
assert (0);
}
}

void push_user (tgl_peer_t *P) {
my_lua_checkstack (luaState, 4);
lua_add_string_field ("first_name", P->user.first_name);
lua_add_string_field ("last_name", P->user.last_name);
lua_add_string_field ("real_first_name", P->user.real_first_name);
lua_add_string_field ("real_last_name", P->user.real_last_name);
lua_add_string_field ("phone", P->user.phone);
lua_add_string_field ("username", P->user.username);
if (P->user.access_hash) {
lua_add_num_field ("access_hash", P->user.access_hash);
}
}

void push_chat (tgl_peer_t *P) {
my_lua_checkstack (luaState, 4);
assert (P->chat.title);
lua_add_string_field ("title", P->chat.title);
lua_add_num_field ("members_num", P->chat.users_num);
if (P->chat.user_list) {
lua_pushstring (luaState, "members");
lua_newtable (luaState);
int i;
for (i = 0; i < P->chat.users_num; i++) {
lua_pushnumber (luaState, i);
tgl_peer_id_t id = TGL_MK_USER (P->chat.user_list[i].user_id);
push_peer (id, tgl_peer_get (TLS, id));
lua_settable (luaState, -3);
}
lua_settable (luaState, -3);
}
}

void push_encr_chat (tgl_peer_t *P) {
my_lua_checkstack (luaState, 4);
lua_pushstring (luaState, "user");
push_peer (TGL_MK_USER (P->encr_chat.user_id), tgl_peer_get (TLS, TGL_MK_USER (P->encr_chat.user_id)));
lua_settable (luaState, -3);
}

void push_channel (tgl_peer_t *P) {
my_lua_checkstack (luaState, 4);
lua_add_string_field ("title", P->channel.title);
lua_add_string_field ("about", P->channel.about);
lua_add_string_field ("username", P->channel.username);
lua_add_num_field ("participants_count", P->channel.participants_count);
lua_add_num_field ("admins_count", P->channel.admins_count);
lua_add_num_field ("kicked_count", P->channel.kicked_count);
}

void push_update_types (unsigned flags) {
my_lua_checkstack (luaState, 4);
lua_newtable (luaState);
int cc = 0;

if (flags & TGL_UPDATE_CREATED) {
lua_add_string_field_arr (cc++, "created");
}
if (flags & TGL_UPDATE_DELETED) {
lua_add_string_field_arr (cc++, "deleted");
}
if (flags & TGL_UPDATE_PHONE) {
lua_add_string_field_arr (cc++, "phone");
}
if (flags & TGL_UPDATE_CONTACT) {
lua_add_string_field_arr (cc++, "contact");
}
if (flags & TGL_UPDATE_PHOTO) {
lua_add_string_field_arr (cc++, "photo");
}
if (flags & TGL_UPDATE_BLOCKED) {
lua_add_string_field_arr (cc++, "blocked");
}
if (flags & TGL_UPDATE_REAL_NAME) {
lua_add_string_field_arr (cc++, "real_name");
}
if (flags & TGL_UPDATE_NAME) {
lua_add_string_field_arr (cc++, "name");
}
if (flags & TGL_UPDATE_REQUESTED) {
lua_add_string_field_arr (cc++, "requested");
}
if (flags & TGL_UPDATE_WORKING) {
lua_add_string_field_arr (cc++, "working");
}
if (flags & TGL_UPDATE_FLAGS) {
lua_add_string_field_arr (cc++, "flags");
}
if (flags & TGL_UPDATE_TITLE) {
lua_add_string_field_arr (cc++, "title");
}
if (flags & TGL_UPDATE_ADMIN) {
lua_add_string_field_arr (cc++, "admin");
}
if (flags & TGL_UPDATE_MEMBERS) {
lua_add_string_field_arr (cc++, "members");
}
if (flags & TGL_UPDATE_ACCESS_HASH) {
lua_add_string_field_arr (cc++, "access_hash");
}
if (flags & TGL_UPDATE_USERNAME) {
lua_add_string_field_arr (cc++, "username");
}

}

void push_peer (tgl_peer_id_t id, tgl_peer_t *P) {
lua_newtable (luaState);

lua_add_string_field ("id", print_permanent_peer_id (P ? P->id : id));
lua_pushstring (luaState, "peer_type");
push_tgl_peer_type (tgl_get_peer_type (id));
lua_settable (luaState, -3);
lua_add_num_field ("peer_id", tgl_get_peer_id (id));

if (!P || !(P->flags & TGLPF_CREATED)) {
lua_pushstring (luaState, "print_name");
static char s[100];
switch (tgl_get_peer_type (id)) {
case TGL_PEER_USER:
sprintf (s, "user#%d", tgl_get_peer_id (id));
break;
case TGL_PEER_CHAT:
sprintf (s, "chat#%d", tgl_get_peer_id (id));
break;
case TGL_PEER_ENCR_CHAT:
sprintf (s, "encr_chat#%d", tgl_get_peer_id (id));
break;
case TGL_PEER_CHANNEL:
sprintf (s, "channel#%d", tgl_get_peer_id (id));
break;
default:
assert (0);
}
lua_pushstring (luaState, s);
lua_settable (luaState, -3); // flags

return;

}

lua_add_string_field ("print_name", P->print_name);
lua_add_num_field ("flags", P->flags);

switch (tgl_get_peer_type (id)) {
case TGL_PEER_USER:
push_user (P);
break;
case TGL_PEER_CHAT:
push_chat (P);
break;
case TGL_PEER_ENCR_CHAT:
push_encr_chat (P);
break;
case TGL_PEER_CHANNEL:
push_channel (P);
break;
default:
assert (0);
}
}

void push_media (struct tgl_message_media *M) {
my_lua_checkstack (luaState, 4);

switch (M->type) {
case tgl_message_media_photo:
lua_newtable (luaState);
lua_add_string_field ("type", "photo");
lua_add_string_field ("caption", M->caption);
break;
case tgl_message_media_document:
lua_newtable (luaState);
lua_add_string_field ("type", "document");
lua_add_string_field ("caption", M->document->caption);
break;
case tgl_message_media_audio:
lua_newtable (luaState);
lua_add_string_field ("type", "audio");
lua_add_string_field ("caption", M->caption);
break;
case tgl_message_media_video:
lua_newtable (luaState);
lua_add_string_field ("type", "video");
lua_add_string_field ("caption", M->caption);
break;
case tgl_message_media_document_encr:
lua_newtable (luaState);
lua_add_string_field ("type", "encr_document");
lua_add_string_field ("caption", M->document->caption);
break;
case tgl_message_media_unsupported:
lua_newtable (luaState);
lua_add_string_field ("type", "unsupported");
break;
case tgl_message_media_geo:
lua_newtable (luaState);
lua_add_string_field ("type", "geo");
lua_add_num_field ("longitude", M->geo.longitude);
lua_add_num_field ("latitude", M->geo.latitude);
break;
case tgl_message_media_contact:
lua_newtable (luaState);
lua_add_string_field ("type", "contact");
lua_add_string_field ("phone", M->phone);
lua_add_string_field ("first_name", M->first_name);
lua_add_string_field ("last_name", M->last_name);
lua_add_num_field ("user_id", M->user_id);
break;
case tgl_message_media_webpage:
lua_newtable (luaState);
lua_add_string_field ("type", "webpage");
lua_add_string_field ("url", M->webpage->url);
lua_add_string_field ("title", M->webpage->title);
lua_add_string_field ("description", M->webpage->description);
lua_add_string_field ("author", M->webpage->author);
break;
case tgl_message_media_venue:
lua_newtable (luaState);
lua_add_string_field ("type", "venue");
lua_add_num_field ("longitude", M->venue.geo.longitude);
lua_add_num_field ("latitude", M->venue.geo.latitude);
lua_add_string_field ("title", M->venue.title);
lua_add_string_field ("address", M->venue.address);
lua_add_string_field ("provider", M->venue.provider);
lua_add_string_field ("venue_id", M->venue.venue_id);
break;
default:
lua_pushstring (luaState, "???");
}
}

void push_service (struct tgl_message *M) {
my_lua_checkstack (luaState, 4);
switch (M->action.type) {
case tgl_message_action_geo_chat_create:
lua_newtable (luaState);
lua_add_string_field ("type", "geo_created");
break;
case tgl_message_action_geo_chat_checkin:
lua_newtable (luaState);
lua_add_string_field ("type", "geo_checkin");
break;
case tgl_message_action_chat_create:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_created");
lua_add_string_field ("title", M->action.title);
break;
case tgl_message_action_chat_edit_title:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_rename");
lua_add_string_field ("title", M->action.title);
break;
case tgl_message_action_chat_edit_photo:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_change_photo");
break;
case tgl_message_action_chat_delete_photo:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_delete_photo");
break;
case tgl_message_action_chat_add_users:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_add_user");

lua_pushstring (luaState, "user");
push_peer (tgl_set_peer_id (TGL_PEER_USER, M->action.users[0]), tgl_peer_get (TLS, tgl_set_peer_id (TGL_PEER_USER, M->action.users[0])));
lua_settable (luaState, -3);
break;

case tgl_message_action_chat_add_user_by_link:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_add_user_link");

lua_pushstring (luaState, "link_issuer");
push_peer (tgl_set_peer_id (TGL_PEER_USER, M->action.user), tgl_peer_get (TLS, tgl_set_peer_id (TGL_PEER_USER, M->action.user)));
lua_settable (luaState, -3);
break;

case tgl_message_action_chat_delete_user:
lua_newtable (luaState);
lua_add_string_field ("type", "chat_del_user");

lua_pushstring (luaState, "user");
push_peer (tgl_set_peer_id (TGL_PEER_USER, M->action.user), tgl_peer_get (TLS, tgl_set_peer_id (TGL_PEER_USER, M->action.user)));
lua_settable (luaState, -3);
break;

case tgl_message_action_set_message_ttl:
lua_newtable (luaState);
lua_add_string_field ("type", "set_ttl");
lua_add_num_field ("ttl", M->action.ttl);
break;
case tgl_message_action_read_messages:
lua_newtable (luaState);
lua_add_string_field ("type", "read");
lua_add_num_field ("count", M->action.read_cnt);
break;
case tgl_message_action_delete_messages:
lua_newtable (luaState);
lua_add_string_field ("type", "delete");
lua_add_num_field ("count", M->action.delete_cnt);
break;
case tgl_message_action_screenshot_messages:
lua_newtable (luaState);
lua_add_string_field ("type", "screenshot");
lua_add_num_field ("count", M->action.screenshot_cnt);
break;
case tgl_message_action_flush_history:
lua_newtable (luaState);
lua_add_string_field ("type", "flush");
break;
case tgl_message_action_resend:
lua_newtable (luaState);
lua_add_string_field ("type", "resend");
break;
case tgl_message_action_notify_layer:
lua_newtable (luaState);
lua_add_string_field ("type", "set_layer");
lua_add_num_field ("layer", M->action.layer);
break;
case tgl_message_action_typing:
lua_newtable (luaState);
lua_add_string_field ("type", "typing");
break;
case tgl_message_action_noop:
lua_newtable (luaState);
lua_add_string_field ("type", "nop");
break;
case tgl_message_action_request_key:
lua_newtable (luaState);
lua_add_string_field ("type", "request_rekey");
break;
case tgl_message_action_accept_key:
lua_newtable (luaState);
lua_add_string_field ("type", "accept_rekey");
break;
case tgl_message_action_commit_key:
lua_newtable (luaState);
lua_add_string_field ("type", "commit_rekey");
break;
case tgl_message_action_abort_key:
lua_newtable (luaState);
lua_add_string_field ("type", "abort_rekey");
break;
case tgl_message_action_channel_create:
lua_newtable (luaState);
lua_add_string_field ("type", "channel_created");
lua_add_string_field ("title", M->action.title);
break;
case tgl_message_action_migrated_to:
lua_newtable (luaState);
lua_add_string_field ("type", "migrated_to");
break;
case tgl_message_action_migrated_from:
lua_newtable (luaState);
lua_add_string_field ("type", "migrated_from");
break;
default:
lua_pushstring (luaState, "???");
break;
}
}

void push_message (struct tgl_message *M) {
assert (M);
my_lua_checkstack (luaState, 10);
lua_newtable (luaState);

lua_add_string_field ("id", print_permanent_msg_id (M->permanent_id));
lua_add_num_field ("temp_id", (M->temp_id));
if (!(M->flags & TGLMF_CREATED)) { return; }
lua_add_num_field ("flags", M->flags);

if (tgl_get_peer_type (M->fwd_from_id)) {
lua_pushstring (luaState, "fwd_from");
push_peer (M->fwd_from_id, tgl_peer_get (TLS, M->fwd_from_id));
lua_settable (luaState, -3); // fwd_from

lua_add_num_field ("fwd_date", M->fwd_date);

}

if (M->reply_id) {
tgl_message_id_t msg_id = M->permanent_id;
msg_id.id = M->reply_id;

lua_add_string_field ("reply_id", print_permanent_msg_id (msg_id));

}

if (M->flags & TGLMF_MENTION) {
lua_pushstring (luaState, "mention");
lua_pushboolean (luaState, 1);
lua_settable (luaState, -3);
}

lua_pushstring (luaState, "from");
push_peer (M->from_id, tgl_peer_get (TLS, M->from_id));
lua_settable (luaState, -3);

lua_pushstring (luaState, "to");
push_peer (M->to_id, tgl_peer_get (TLS, M->to_id));
lua_settable (luaState, -3);

lua_pushstring (luaState, "out");
lua_pushboolean (luaState, (M->flags & TGLMF_OUT) != 0);
lua_settable (luaState, -3);

lua_pushstring (luaState, "unread");
lua_pushboolean (luaState, (M->flags & TGLMF_UNREAD) != 0);
lua_settable (luaState, -3);

lua_pushstring (luaState, "date");
lua_pushnumber (luaState, M->date);
lua_settable (luaState, -3);

lua_pushstring (luaState, "service");
lua_pushboolean (luaState, (M->flags & TGLMF_SERVICE) != 0);
lua_settable (luaState, -3);

if (!(M->flags & TGLMF_SERVICE)) {
if (M->message_len && M->message) {
lua_pushstring (luaState, "text");
lua_pushlstring (luaState, M->message, M->message_len);
lua_settable (luaState, -3);
}
if (M->media.type && M->media.type != tgl_message_media_none) {
lua_pushstring (luaState, "media");
push_media (&M->media);
lua_settable (luaState, -3);
}
} else {
lua_pushstring (luaState, "action");
push_service (M);
lua_settable (luaState, -3);
}
}

void lua_binlog_end (void) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_binlog_replay_end");
assert (lua_gettop (luaState) == 1);

int r = ps_lua_pcall (luaState, 0, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void lua_diff_end (void) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_get_difference_end");
assert (lua_gettop (luaState) == 1);

int r = ps_lua_pcall (luaState, 0, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void lua_our_id (tgl_peer_id_t id) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_our_id");
lua_pushnumber (luaState, tgl_get_peer_id (id));
assert (lua_gettop (luaState) == 2);

int r = ps_lua_pcall (luaState, 1, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void lua_new_msg (struct tgl_message *M) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_msg_receive");
push_message (M);
assert (lua_gettop (luaState) == 2);

int r = ps_lua_pcall (luaState, 1, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void lua_secret_chat_update (struct tgl_secret_chat *C, unsigned flags) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_secret_chat_update");
push_peer (C->id, (void *)C);
push_update_types (flags);
assert (lua_gettop (luaState) == 3);

int r = ps_lua_pcall (luaState, 2, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void lua_user_update (struct tgl_user *U, unsigned flags) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_user_update");
push_peer (U->id, (void *)U);
push_update_types (flags);
assert (lua_gettop (luaState) == 3);

int r = ps_lua_pcall (luaState, 2, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void lua_chat_update (struct tgl_chat *C, unsigned flags) {
if (!have_file) { return; }
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);
lua_getglobal (luaState, "on_chat_update");
push_peer (C->id, (void *)C);
push_update_types (flags);
assert (lua_gettop (luaState) == 3);

int r = ps_lua_pcall (luaState, 2, 0, 0);
if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

//extern tgl_peer_t *Peers[];
//extern int peer_num;

#define MAX_LUA_COMMANDS 1000

struct lua_arg {
int flags;
union {
tgl_message_id_t msg_id;
tgl_peer_id_t peer_id;
char *str;
long long num;
double dnum;
void *ptr;
};
};
struct lua_arg lua_ptr[MAX_LUA_COMMANDS];
static int pos;

static inline tgl_peer_t *get_peer (const char *s) {
return tgl_peer_get_by_name (TLS, s);
}

enum lua_query_type {
lq_contact_list,
lq_dialog_list,
lq_msg,
lq_msg_channel,
lq_send_typing,
lq_send_typing_abort,
lq_rename_chat,
lq_send_photo,
lq_send_photo2,
lq_chat_set_photo,
lq_set_profile_photo,
lq_set_profile_name,
lq_set_profile_username,
lq_send_video,
lq_send_text,
lq_reply,
lq_fwd,
lq_fwd_media,
lq_load_photo,
lq_load_video_thumb,
lq_load_video,
lq_chat_info,
lq_user_info,
lq_history,
lq_chat_add_user,
lq_chat_del_user,
lq_add_contact,
lq_del_contact,
lq_rename_contact,
lq_search,
lq_global_search,
lq_resolve_username,
lq_mark_read,
lq_create_secret_chat,
lq_create_group_chat,
lq_send_audio,
lq_send_document,
lq_send_file,
lq_load_audio,
lq_load_document,
lq_load_document_thumb,
lq_delete_msg,
lq_restore_msg,
lq_get_message,
lq_accept_secret_chat,
lq_send_contact,
lq_status_online,
lq_status_offline,
lq_send_location,
lq_extf,
lq_import_chat_link,
lq_export_chat_link,
lq_channels_dialog_list,
lq_chat_upgrade,
lq_create_channel,
lq_channel_info,
lq_export_channel_link,
lq_channel_invite,
lq_channel_join,
lq_leave_channel,
lq_channel_kick,
lq_channel_get_admins,
lq_channel_get_users,
lq_channel_get_bots,
lq_channel_get_kicked,
lq_channel_unblock,
lq_rename_channel,
lq_channel_set_photo,
lq_channel_set_about,
lq_channel_set_username,
lq_channel_set_admin,
lq_channel_set_mod,
lq_channel_demote,
lq_reply_location,
lq_reply_audio,
lq_reply_document,
lq_reply_file,
lq_reply_photo,
lq_reply_photo2,
lq_block_user,
lq_unblock_user,
lq_reply_video
};

struct lua_query_extra {
int func;
int param;
};

void lua_empty_cb (struct tgl_state *TLSR, void *cb_extra, int success) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

assert (lua_gettop (luaState) == 3);

int r = ps_lua_pcall (luaState, 2, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_contact_list_cb (struct tgl_state *TLSR, void *cb_extra, int success, int num, struct tgl_user **UL) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
lua_newtable (luaState);
int i;
for (i = 0; i < num; i++) {
lua_pushnumber (luaState, i);
push_peer (UL[i]->id, (void *)UL[i]);
lua_settable (luaState, -3);
}
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_dialog_list_cb (struct tgl_state *TLSR, void *cb_extra, int success, int num, tgl_peer_id_t peers[], tgl_message_id_t *msgs[], int unread[]) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);
if (success) {
lua_newtable (luaState);
int i;
for (i = 0; i < num; i++) {
lua_pushnumber (luaState, i);

  lua_newtable (luaState);

  lua_pushstring (luaState, "peer");
  push_peer (peers[i], tgl_peer_get (TLS, peers[i]));
  lua_settable (luaState, -3);

  struct tgl_message *M = tgl_message_get (TLS, msgs[i]);
  if (M && (M->flags & TGLMF_CREATED)) {
    lua_pushstring (luaState, "message");
    push_message (M);
    lua_settable (luaState, -3);
  }

  lua_pushstring (luaState, "unread");
  lua_pushnumber (luaState, unread[i]);
  lua_settable (luaState, -3);

  lua_settable (luaState, -3);
}

} else {
lua_pushboolean (luaState, 0);
}
assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_msg_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_message *M) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success && M && (M->flags & TGLMF_CREATED)) {
push_message (M);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_one_msg_cb (struct tgl_state *TLSR, void *cb_extra, int success, int size, struct tgl_message *M[]) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success && size > 0 && M[0] && (M[0]->flags & TGLMF_CREATED)) {
push_message (M[0]);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_msg_list_cb (struct tgl_state *TLSR, void *cb_extra, int success, int num, struct tgl_message *M[]) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
lua_newtable (luaState);
int i;
for (i = 0; i < num; i++) {
lua_pushnumber (luaState, i);
push_message (M[i]);
lua_settable (luaState, -3);
}
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_file_cb (struct tgl_state *TLSR, void *cb_extra, int success, const char *file_name) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
lua_pushstring (luaState, file_name);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_chat_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_chat *C) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
push_peer (C->id, (void *)C);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_secret_chat_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_secret_chat *C) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
push_peer (C->id, (void *)C);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_channel_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_channel *C) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
push_peer (C->id, (void *)C);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_user_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_user *C) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
push_peer (C->id, (void *)C);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_str_cb (struct tgl_state *TLSR, void *cb_extra, int success, const char *data) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
lua_pushstring (luaState, data);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

#define LUA_STR_ARG(n) lua_ptr[n].str, strlen (lua_ptr[n].str)

void lua_contact_search_cb (struct tgl_state *TLSR, void *cb_extra, int success, tgl_peer_t *C) {
assert (TLSR == TLS);
struct lua_query_extra *cb = cb_extra;
lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, cb->param);

lua_pushnumber (luaState, success);

if (success) {
push_peer (C->id, (void *)C);
} else {
lua_pushboolean (luaState, 0);
}

assert (lua_gettop (luaState) == 4);

int r = ps_lua_pcall (luaState, 3, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, cb->func);
luaL_unref (luaState, LUA_REGISTRYINDEX, cb->param);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

free (cb);
}

void lua_do_all (void) {
int p = 0;
while (p < pos) {
int l = lua_ptr[p ++].num;
assert (p + l + 1 <= pos);
enum lua_query_type f = lua_ptr[p ++].num;
struct tgl_message *M;
int q = p;
tgl_message_id_t *tmp_msg_id;
switch (f) {
case lq_contact_list:
tgl_do_update_contact_list (TLS, lua_contact_list_cb, lua_ptr[p ++].ptr);
break;
case lq_dialog_list:
tgl_do_get_dialog_list (TLS, 100, 0, lua_dialog_list_cb, lua_ptr[p ++].ptr);
break;
case lq_msg:
tgl_do_send_message (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), TGLMF_HTML, NULL, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_msg_channel:
tgl_do_send_message (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), 256, NULL, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_typing:
tgl_do_send_typing (TLS, lua_ptr[p + 1].peer_id, tgl_typing_typing, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_send_typing_abort:
tgl_do_send_typing (TLS, lua_ptr[p + 1].peer_id, tgl_typing_cancel, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_rename_chat:
tgl_do_rename_chat (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_photo:
tgl_do_send_document (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_PHOTO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_photo2:
tgl_do_send_document (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, lua_ptr[p + 3].str, strlen(lua_ptr[p + 3].str), TGL_SEND_MSG_FLAG_DOCUMENT_PHOTO, lua_msg_cb, lua_ptr[p].ptr);
p += 4;
break;
case lq_send_video:
tgl_do_send_document (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_VIDEO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_audio:
tgl_do_send_document (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_AUDIO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_document:
tgl_do_send_document (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, NULL, 0, 0, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_file:
tgl_do_send_document (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_AUTO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_send_text:
tgl_do_send_text (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, 0, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_chat_set_photo:
tgl_do_set_chat_photo (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;

case lq_load_photo:
case lq_load_video:
case lq_load_audio:
case lq_load_document:
  M = tgl_message_get (TLS, &lua_ptr[p + 1].msg_id);
  if (!M || (M->media.type != tgl_message_media_photo && M->media.type != tgl_message_media_document && M->media.type != tgl_message_media_video && M->media.type != tgl_message_media_audio && M->media.type != tgl_message_media_document_encr)) {
    lua_file_cb (TLS, lua_ptr[p].ptr, 0, 0);
  } else {
    if (M->media.type == tgl_message_media_photo) {
      assert (M->media.photo);
      tgl_do_load_photo (TLS, M->media.photo, lua_file_cb, lua_ptr[p].ptr);
    } else if (M->media.type == tgl_message_media_document) {
      assert (M->media.document);
      tgl_do_load_document (TLS, M->media.document, lua_file_cb, lua_ptr[p].ptr);
	} else if (M->media.type == tgl_message_media_video) {
      assert (M->media.document);
      tgl_do_load_audio (TLS, M->media.document, lua_file_cb, lua_ptr[p].ptr);
	} else if (M->media.type == tgl_message_media_audio) {
      assert (M->media.document);
      tgl_do_load_audio (TLS, M->media.document, lua_file_cb, lua_ptr[p].ptr);
    } else {
      tgl_do_load_encr_document (TLS, M->media.encr_document, lua_file_cb, lua_ptr[p].ptr);
    }
  }
  p += 2;
  break;
case lq_load_video_thumb:
case lq_load_document_thumb:
  M = tgl_message_get (TLS, &lua_ptr[p + 1].msg_id);
  if (!M || (M->media.type != tgl_message_media_document)) {
    lua_file_cb (TLS, lua_ptr[p].ptr, 0, 0);
  } else {
    tgl_do_load_document_thumb (TLS, M->media.document, lua_file_cb, lua_ptr[p].ptr);
  }
  p += 2;
  break;

case lq_reply:
tgl_do_reply_message (TLS, &lua_ptr[p + 1].msg_id, LUA_STR_ARG (p + 2), TGLMF_HTML, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_fwd:
tmp_msg_id = &lua_ptr[p + 2].msg_id;
tgl_do_forward_messages (TLS, lua_ptr[p + 1].peer_id, 1, (void *)&tmp_msg_id, 0, lua_one_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_fwd_media:
tgl_do_forward_media (TLS, lua_ptr[p + 1].peer_id, &lua_ptr[p + 2].msg_id, 0, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_chat_info:
tgl_do_get_chat_info (TLS, lua_ptr[p + 1].peer_id, 0, lua_chat_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_user_info:
tgl_do_get_user_info (TLS, lua_ptr[p + 1].peer_id, 0, lua_user_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_history:
tgl_do_get_history (TLS, lua_ptr[p + 1].peer_id, 0, lua_ptr[p + 2].num, 0, lua_msg_list_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_chat_add_user:
tgl_do_add_user_to_chat (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, 10, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_chat_del_user:
tgl_do_del_user_from_chat (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_add_contact:
tgl_do_add_contact (TLS, LUA_STR_ARG (p + 1), LUA_STR_ARG (p + 2), LUA_STR_ARG (p + 3), 0, lua_contact_list_cb, lua_ptr[p].ptr);
p += 4;
break;
case lq_del_contact:
tgl_do_del_contact (TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_rename_contact:
tgl_do_add_contact (TLS, LUA_STR_ARG (p + 1), LUA_STR_ARG (p + 2), LUA_STR_ARG (p + 3), 1, lua_contact_list_cb, lua_ptr[p].ptr);
p += 4;
break;
case lq_search:
tgl_do_msg_search (TLS, lua_ptr[p + 1].peer_id, 0, 0, 40, 0, LUA_STR_ARG (p + 2), lua_msg_list_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_global_search:
tgl_do_msg_search (TLS, tgl_set_peer_id (TGL_PEER_UNKNOWN, 0), 0, 0, 40, 0, LUA_STR_ARG (p + 1), lua_msg_list_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_resolve_username:
tgl_do_contact_search (TLS, LUA_STR_ARG (p + 1), lua_contact_search_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_mark_read:
tgl_do_mark_read (TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_set_profile_photo:
tgl_do_set_profile_photo (TLS, lua_ptr[p + 1].str, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_set_profile_username:
tgl_do_set_username (TLS, lua_ptr[p + 1].str, strlen(lua_ptr[p + 1].str), lua_user_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_set_profile_name:
tgl_do_set_profile_name (TLS, lua_ptr[p + 1].str, strlen(lua_ptr[p + 1].str),lua_ptr[p + 2].str, strlen(lua_ptr[p + 2].str), lua_user_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_create_secret_chat:
tgl_do_create_secret_chat (TLS, lua_ptr[p + 1].peer_id, lua_secret_chat_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_block_user:
//tgl_do_block_user(TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
tgl_do_block_user(TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_unblock_user:
//tgl_do_block_user(TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
tgl_do_unblock_user(TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_create_group_chat:
tgl_do_create_group_chat (TLS, 1, &lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_delete_msg:
tgl_do_delete_msg (TLS, &lua_ptr[p + 1].msg_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_get_message:
tgl_do_get_message (TLS, &lua_ptr[p + 1].msg_id, lua_msg_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_accept_secret_chat:
tgl_do_accept_encr_chat_request (TLS, (void )tgl_peer_get (TLS, lua_ptr[p + 1].peer_id), lua_secret_chat_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_send_contact:
tgl_do_send_contact (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), LUA_STR_ARG (p + 3), LUA_STR_ARG (p + 4), 0, lua_msg_cb, lua_ptr[p].ptr);
p += 5;
break;
case lq_status_online:
tgl_do_update_status (TLS, 1, lua_empty_cb, lua_ptr[p].ptr);
p ++;
break;
case lq_status_offline:
tgl_do_update_status (TLS, 0, lua_empty_cb, lua_ptr[p].ptr);
p ++;
break;
case lq_extf:
tgl_do_send_extf (TLS, LUA_STR_ARG (p + 1), lua_str_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_import_chat_link:
tgl_do_import_chat_link (TLS, LUA_STR_ARG (p + 1), lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_export_chat_link:
tgl_do_export_chat_link (TLS, lua_ptr[p + 1].peer_id, lua_str_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_send_location:
tgl_do_send_location (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].dnum, lua_ptr[p + 3].dnum, 0, lua_msg_cb, lua_ptr[p].ptr);
p += 4;
break;
//channel Support
case lq_channels_dialog_list:
tgl_do_get_channels_dialog_list (TLS, 100, 0, lua_dialog_list_cb, lua_ptr[p ++].ptr);
break;
case lq_chat_upgrade:
tgl_do_upgrade_group (TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_create_channel:
tgl_do_create_channel (TLS, 1, &lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), LUA_STR_ARG (p + 3), 1,lua_empty_cb, lua_ptr[p].ptr);
p += 4;
break;
case lq_channel_info:
tgl_do_get_channel_info (TLS, lua_ptr[p + 1].peer_id, 0, lua_channel_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_export_channel_link:
tgl_do_export_channel_link (TLS, lua_ptr[p + 1].peer_id, lua_str_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_channel_invite:
tgl_do_channel_invite_user (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_join:
tgl_do_join_channel (TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_leave_channel:
tgl_do_leave_channel (TLS, lua_ptr[p + 1].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_channel_kick:
tgl_do_channel_kick_user (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_get_admins:
tgl_do_channel_get_members (TLS, lua_ptr[p + 1].peer_id, 100, 0, 1, lua_contact_list_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_channel_get_users:
tgl_do_channel_get_members (TLS, lua_ptr[p + 1].peer_id, 10000, 0, 0, lua_contact_list_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_channel_get_bots:
tgl_do_channel_get_members (TLS, lua_ptr[p + 1].peer_id, 5000, 0, 4, lua_contact_list_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_channel_get_kicked:
tgl_do_channel_get_members (TLS, lua_ptr[p + 1].peer_id, 5000, 0, 3, lua_contact_list_cb, lua_ptr[p].ptr);
p += 2;
break;
case lq_channel_unblock:
tgl_do_channel_set_admin (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, 0, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_rename_channel:
tgl_do_rename_channel (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_set_photo:
tgl_do_set_channel_photo (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].str, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_set_about:
tgl_do_channel_set_about (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_set_username:
tgl_do_channel_set_username (TLS, lua_ptr[p + 1].peer_id, LUA_STR_ARG (p + 2), lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_set_admin:
tgl_do_channel_set_admin (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, 2, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_set_mod:
tgl_do_channel_set_admin (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, 1, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_channel_demote:
tgl_do_channel_set_admin (TLS, lua_ptr[p + 1].peer_id, lua_ptr[p + 2].peer_id, 0, lua_empty_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_reply_audio:
tgl_do_reply_document(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_AUDIO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_reply_document:
tgl_do_reply_document(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].str, NULL, 0, 0, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_reply_file:
tgl_do_reply_document(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_AUTO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_reply_location: // TODO - I DON'T UNDERSTAND WHY IT'S NOT WORKING
tgl_do_reply_location(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].dnum, lua_ptr[p + 3].dnum, 0, lua_msg_cb, lua_ptr[p].ptr);
p += 4;
break;
case lq_reply_photo:
tgl_do_reply_document(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_PHOTO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_reply_photo2:
tgl_do_reply_document(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].str, lua_ptr[p + 3].str, strlen(lua_ptr[p + 3].str), TGL_SEND_MSG_FLAG_DOCUMENT_PHOTO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
case lq_reply_video:
tgl_do_reply_document(TLS, &lua_ptr[p + 1].msg_id, lua_ptr[p + 2].str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_VIDEO, lua_msg_cb, lua_ptr[p].ptr);
p += 3;
break;
/

lq_delete_msg,
lq_restore_msg,
case 0:
tgl_do_send_message (((tgl_peer_t *)lua_ptr[p])->id, lua_ptr[p + 1], strlen (lua_ptr[p + 1]), 0, 0);
free (lua_ptr[p + 1]);
p += 2;
break;
case 1:
tgl_do_forward_message (((tgl_peer_t *)lua_ptr[p])->id, (long)lua_ptr[p + 1], 0, 0);
p += 2;
break;
case 2:
tgl_do_mark_read (((tgl_peer_t )lua_ptr[p])->id, 0, 0);
p += 1;
break;
/
default:
assert (0);
}
while (q < p) {
if (lua_ptr[q].flags & 1) {
tfree_str (lua_ptr[q].str);
}
q ++;
}
}
pos = 0;
}

enum lua_function_param {
lfp_none,
lfp_peer,
lfp_chat,
lfp_channel,
lfp_user,
lfp_secret_chat,
lfp_string,
lfp_number,
lfp_positive_number,
lfp_nonnegative_number,
lfp_msg,
lfp_double
};

struct lua_function {
char *name;
enum lua_query_type type;
enum lua_function_param params[10];
};

struct lua_function functions[] = {
{"get_contact_list", lq_contact_list, { lfp_none }},
{"get_dialog_list", lq_dialog_list, { lfp_none }},
{"rename_chat", lq_rename_chat, { lfp_chat, lfp_string, lfp_none }},
{"send_msg", lq_msg, { lfp_peer, lfp_string, lfp_none }},
{"post_msg", lq_msg_channel, { lfp_channel, lfp_string, lfp_none }},
{"send_typing", lq_send_typing, { lfp_peer, lfp_none }},
{"send_typing_abort", lq_send_typing_abort, { lfp_peer, lfp_none }},
{"send_photo", lq_send_photo, { lfp_peer, lfp_string, lfp_none }},
{"send_photo2", lq_send_photo2, { lfp_peer, lfp_string, lfp_string, lfp_none }},
{"send_video", lq_send_video, { lfp_peer, lfp_string, lfp_none }},
{"send_audio", lq_send_audio, { lfp_peer, lfp_string, lfp_none }},
{"send_document", lq_send_document, { lfp_peer, lfp_string, lfp_none }},
{"send_file", lq_send_file, { lfp_peer, lfp_string, lfp_none }},
{"send_text", lq_send_text, { lfp_peer, lfp_string, lfp_none }},
{"chat_set_photo", lq_chat_set_photo, { lfp_chat, lfp_string, lfp_none }},
{"load_photo", lq_load_photo, { lfp_msg, lfp_none }},
{"load_video", lq_load_video, { lfp_msg, lfp_none }},
{"load_video_thumb", lq_load_video_thumb, { lfp_msg, lfp_none }},
{"load_audio", lq_load_audio, { lfp_msg, lfp_none }},
{"load_document", lq_load_document, { lfp_msg, lfp_none }},
{"load_document_thumb", lq_load_document_thumb, { lfp_msg, lfp_none }},
{"reply_msg", lq_reply, { lfp_msg, lfp_string, lfp_none }},
{"fwd_msg", lq_fwd, { lfp_peer, lfp_msg, lfp_none }},
{"fwd_media", lq_fwd_media, { lfp_peer, lfp_msg, lfp_none }},
{"chat_info", lq_chat_info, { lfp_chat, lfp_none }},
{"channel_info", lq_channel_info, { lfp_channel, lfp_none }},
{"user_info", lq_user_info, { lfp_user, lfp_none }},
{"get_history", lq_history, { lfp_peer, lfp_nonnegative_number, lfp_none }},
{"chat_add_user", lq_chat_add_user, { lfp_chat, lfp_user, lfp_none }},
{"chat_del_user", lq_chat_del_user, { lfp_chat, lfp_user, lfp_none }},
{"add_contact", lq_add_contact, { lfp_string, lfp_string, lfp_string, lfp_none }},
{"del_contact", lq_del_contact, { lfp_user, lfp_none }},
{"rename_contact", lq_rename_contact, { lfp_string, lfp_string, lfp_string, lfp_none }},
{"msg_search", lq_search, { lfp_peer, lfp_string, lfp_none }},
{"msg_global_search", lq_global_search, { lfp_string, lfp_none }},
{"resolve_username", lq_resolve_username, { lfp_string, lfp_none }},
{"mark_read", lq_mark_read, { lfp_peer, lfp_none }},
{"set_profile_photo", lq_set_profile_photo, { lfp_string, lfp_none }},
{"set_profile_name", lq_set_profile_name, { lfp_string,lfp_string, lfp_none }},
{"set_profile_username", lq_set_profile_username, { lfp_string, lfp_none }},
{"create_secret_chat", lq_create_secret_chat, { lfp_user, lfp_none }},
{"create_group_chat", lq_create_group_chat, { lfp_user, lfp_string, lfp_none }},
{"delete_msg", lq_delete_msg, { lfp_msg, lfp_none }},
{"restore_msg", lq_restore_msg, { lfp_positive_number, lfp_none }},
{"get_message", lq_get_message, { lfp_msg, lfp_none }},
{"accept_secret_chat", lq_accept_secret_chat, { lfp_secret_chat, lfp_none }},
{"send_contact", lq_send_contact, { lfp_peer, lfp_string, lfp_string, lfp_string, lfp_none }},
{"status_online", lq_status_online, { lfp_none }},
{"status_offline", lq_status_offline, { lfp_none }},
{"send_location", lq_send_location, { lfp_peer, lfp_double, lfp_double, lfp_none }},
{"ext_function", lq_extf, { lfp_string, lfp_none }},
{"import_chat_link", lq_import_chat_link, { lfp_string, lfp_none }},
{"export_chat_link", lq_export_chat_link, { lfp_chat, lfp_none }},
{"get_channels_dialog_list", lq_channels_dialog_list, { lfp_none }},
{"chat_upgrade", lq_chat_upgrade, { lfp_peer, lfp_none }},
{"create_channel", lq_create_channel, { lfp_peer, lfp_string, lfp_string, lfp_none }},
{"channel_info", lq_channel_info, { lfp_channel, lfp_none }},
{"export_channel_link", lq_export_channel_link, { lfp_channel, lfp_none }},
{"channel_invite", lq_channel_invite, { lfp_channel, lfp_user, lfp_none }},
{"channel_join", lq_channel_join, { lfp_channel, lfp_none }},
{"leave_channel", lq_leave_channel, { lfp_channel, lfp_none }},
{"channel_kick", lq_channel_kick, { lfp_channel, lfp_user, lfp_none }},
{"channel_get_admins", lq_channel_get_admins, { lfp_channel, lfp_none }},
{"channel_get_users", lq_channel_get_users, { lfp_channel, lfp_none }},
{"channel_get_bots", lq_channel_get_bots, { lfp_channel, lfp_none }},
{"channel_get_kicked", lq_channel_get_kicked, { lfp_channel, lfp_none }},
{"channel_unblock", lq_channel_unblock, { lfp_channel, lfp_peer, lfp_none }},
{"rename_channel", lq_rename_channel, { lfp_channel, lfp_string, lfp_none }},
{"channel_set_photo", lq_channel_set_photo, { lfp_channel, lfp_string, lfp_none }},
{"channel_set_about", lq_channel_set_about, { lfp_channel, lfp_string, lfp_none }},
{"channel_set_username", lq_channel_set_username, { lfp_channel, lfp_string, lfp_none }},
{"channel_set_admin", lq_channel_set_admin, { lfp_channel, lfp_peer, lfp_none }},
{"channel_set_mod", lq_channel_set_mod, { lfp_channel, lfp_peer, lfp_none }},
{"channel_demote", lq_channel_demote, { lfp_channel, lfp_peer, lfp_none }},
{ "reply_msg", lq_reply, { lfp_msg, lfp_string, lfp_none } },
{ "reply_file", lq_reply_file, { lfp_msg, lfp_string, lfp_none } },
{ "reply_audio", lq_send_audio, { lfp_msg, lfp_string, lfp_none } },
{ "reply_location", lq_reply_location, { lfp_msg, lfp_double, lfp_double, lfp_none } },
{ "reply_document", lq_reply_document, { lfp_msg, lfp_string, lfp_none } },
{ "reply_photo", lq_reply_photo, { lfp_msg, lfp_string, lfp_none } },
{ "block_user", lq_block_user, { lfp_user, lfp_none } },
{ "unblock_user", lq_unblock_user, { lfp_user, lfp_none } },
{ "reply_photo2", lq_reply_photo, { lfp_msg, lfp_string, lfp_string, lfp_none } },
{ "reply_video", lq_reply_video, { lfp_msg, lfp_string, lfp_none } },

{ 0, 0, { lfp_none}}
};

static int parse_lua_function (lua_State *L, struct lua_function *F) {
int p = 0;
while (F->params[p] != lfp_none) { p ++; }
if (lua_gettop (L) != p + 2) {
lua_pushboolean (L, 0);
return 1;
}

int a1 = luaL_ref (L, LUA_REGISTRYINDEX);
int a2 = luaL_ref (L, LUA_REGISTRYINDEX);

struct lua_query_extra *e = malloc (sizeof (*e));
assert (e);
e->func = a2;
e->param = a1;

assert (pos + 3 + p < MAX_LUA_COMMANDS);

lua_ptr[pos ++].num = (p + 1);
lua_ptr[pos ++].num = F->type;
lua_ptr[pos ++].ptr = e;

int sp = p;
int ok = 1;
int cc = 0;
while (p > 0) {
p --;
cc ++;
const char *s;
long long num;
double dval;
tgl_peer_id_t peer_id;
lua_ptr[pos + p].flags = 0;
switch (F->params[p]) {
case lfp_none:
assert (0);
break;
case lfp_peer:
case lfp_user:
case lfp_chat:
case lfp_channel:
case lfp_secret_chat:
s = lua_tostring (L, -cc);
if (!s) {
ok = 0;
break;
}

  if (F->params[p] == lfp_user) {
    peer_id = parse_input_peer_id (s, strlen (s), TGL_PEER_USER);
  } else if (F->params[p] == lfp_chat) {
    peer_id = parse_input_peer_id (s, strlen (s), TGL_PEER_CHAT);
  } else if (F->params[p] == lfp_secret_chat) {
    peer_id = parse_input_peer_id (s, strlen (s), TGL_PEER_ENCR_CHAT);
  } else if (F->params[p] == lfp_channel) {
    peer_id = parse_input_peer_id (s, strlen (s), TGL_PEER_CHANNEL);
  } else {
    peer_id = parse_input_peer_id (s, strlen (s), 0);
  }

  if (!peer_id.peer_type) {
    ok = 0;
    break;
  }

  lua_ptr[pos + p].peer_id = peer_id;
  break;

case lfp_string:
  s = lua_tostring (L, -cc);
  if (!s) {
    ok = 0;
    break;
  }
  lua_ptr[pos + p].str = (void *)s;
  lua_ptr[pos + p].flags |= 1;
  break;

case lfp_number:
  num = lua_tonumber (L, -cc);

  lua_ptr[pos + p].num = num;
  break;

case lfp_double:
  dval = lua_tonumber (L, -cc);
  lua_ptr[pos + p].dnum = dval;
  break;

case lfp_msg:
  s = lua_tostring (L, -cc);
  if (!s) {
    ok = 0;
    break;
  }
  lua_ptr[pos + p].msg_id = parse_input_msg_id (s, strlen (s));
  if (lua_ptr[pos + p].msg_id.peer_type == 0) {
    ok = 0;
    break;
  }
  break;

case lfp_positive_number:
  num = lua_tonumber (L, -cc);
  if (num <= 0) {
    ok = 0;
    break;
  }

  lua_ptr[pos + p].num = num;
  break;

case lfp_nonnegative_number:
  num = lua_tonumber (L, -cc);
  if (num < 0) {
    ok = 0;
    break;
  }

  lua_ptr[pos + p].num = num;
  break;

default:
  assert (0);
}

}
if (!ok) {
luaL_unref (luaState, LUA_REGISTRYINDEX, a1);
luaL_unref (luaState, LUA_REGISTRYINDEX, a2);
free (e);
pos -= 3;
lua_pushboolean (L, 0);
return 1;
}

for (p = 0; p < sp; p++) {
if (F->params[p] == lfp_string) {
lua_ptr[pos + p].str = tstrdup (lua_ptr[pos + p].str);
}
}
pos += p;

lua_pushboolean (L, 1);
return 1;
}

static void lua_postpone_alarm (evutil_socket_t fd, short what, void *arg) {
int *t = arg;

lua_settop (luaState, 0);
//lua_checkstack (luaState, 20);
my_lua_checkstack (luaState, 20);

lua_rawgeti (luaState, LUA_REGISTRYINDEX, t[1]);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, t[0]);
assert (lua_gettop (luaState) == 2);

int r = ps_lua_pcall (luaState, 1, 0, 0);

luaL_unref (luaState, LUA_REGISTRYINDEX, t[0]);
luaL_unref (luaState, LUA_REGISTRYINDEX, t[1]);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}

}

static int postpone_from_lua (lua_State *L) {
int n = lua_gettop (L);
if (n != 3) {
lua_pushboolean (L, 0);
return 1;
}

double timeout = lua_tonumber (L, -1);
if (timeout < 0) {
lua_pushboolean (L, 0);
return 1;
}

lua_pop (L, 1);
int a1 = luaL_ref (L, LUA_REGISTRYINDEX);
int a2 = luaL_ref (L, LUA_REGISTRYINDEX);

int *t = malloc (16);
assert (t);
struct event *ev = evtimer_new (TLS->ev_base, lua_postpone_alarm, t);
t[0] = a1;
t[1] = a2;
*(void **)(t + 2) = ev;

struct timeval ts= {
.tv_sec = (long)timeout,
.tv_usec = (timeout - ((long)timeout)) * 1000000
};
event_add (ev, &ts);

lua_pushboolean (L, 1);
return 1;
}

extern int safe_quit;
static int safe_quit_from_lua (lua_State *L) {
int n = lua_gettop (L);
if (n != 0) {
lua_pushboolean (L, 0);
return 1;
}
safe_quit = 1;

lua_pushboolean (L, 1);
return 1;
}

static int universal_from_lua (lua_State *L) {
const char *s = lua_tostring(L, lua_upvalueindex(1));
if (!s) {
lua_pushboolean (L, 0);
return 1;
}
int i = 0;
while (functions[i].name) {
if (!strcmp (functions[i].name, s)) {
return parse_lua_function (L, &functions[i]);
}
i ++;
}
lua_pushboolean (L, 0);
return 1;
}

static void my_lua_register (lua_State *L, const char *name, lua_CFunction f) {
lua_pushstring(L, name);
lua_pushcclosure(L, f, 1);
lua_setglobal(L, name);
}

enum command_argument {
ca_none,
ca_user,
ca_chat,
ca_secret_chat,
ca_channel,
ca_peer,
ca_file_name,
ca_file_name_end,
ca_period,
ca_number,
ca_double,
ca_string_end,
ca_string,
ca_modifier,
ca_command,
ca_extf,

ca_optional = 256
};

struct arg {
int flags;
struct {
tgl_peer_t *P;
struct tgl_message *M;
char *str;
long long num;
double dval;
};
};

struct in_ev;
struct command {
char *name;
enum command_argument args[10];
void (*fun)(struct command *command, int arg_num, struct arg args[], struct in_ev *ev);
char *desc;
void *arg;
};

#define NOT_FOUND (int)0x80000000

static void do_interface_from_lua (struct command *command, int arg_num, struct arg args[], struct in_ev *ev) {
lua_settop (luaState, 0);
my_lua_checkstack (luaState, 20);

struct lua_query_extra *e = command->arg;
lua_rawgeti (luaState, LUA_REGISTRYINDEX, e->func);
lua_rawgeti (luaState, LUA_REGISTRYINDEX, e->param);

int i;
for (i = 0; i < arg_num; i ++) {
int j = i;
if (j > 9) { j = 9; }
while (j >= 0) {
if (command->args[j] == ca_period) { j --; continue; }
if (command->args[j] == ca_none) { j --; continue; }
break;
}
assert (j >= 0);

switch (command->args[j] & 0xff) {
case ca_none:
case ca_period:
  assert (0);
  break;
case ca_user:
case ca_chat:
case ca_secret_chat:
case ca_peer:
  if (args[i].P) {
    push_peer (args[i].P->id, args[i].P);
  } else {
    lua_pushnil (luaState);
  }
  break;
case ca_file_name:
case ca_file_name_end:
case ca_string_end:
case ca_string:
  if (args[i].str) {
    lua_pushstring (luaState, args[i].str);
  } else {
    lua_pushnil (luaState);
  }
  break;
case ca_number:
  if (args[i].num != NOT_FOUND) {
    lua_pushnumber (luaState, args[i].num);
  } else {
    lua_pushnil (luaState);
  }
  break;
case ca_double:
  if (args[i].dval != NOT_FOUND) {
    lua_pushnumber (luaState, args[i].dval);
  } else {
    lua_pushnil (luaState);
  }
  break;
}

}

int r = ps_lua_pcall (luaState, 1 + arg_num, 0, 0);

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
}
}

void register_new_command (struct command *cmd);
static int register_interface_from_lua (lua_State *L) {
int n = lua_gettop (L);
if (n <= 4 || n >= 13) {
lua_pushboolean (L, 0);
return 1;
}

static struct command cmd;
memset (&cmd, 0, sizeof (struct command));

int i;
for (i = 0; i < n - 4; i++) {
const char *s = lua_tostring (L, -1);
lua_pop (L, 1);

if (!s || !strlen (s)) {
  lua_pushboolean (L, 0);
  return 1;
}

int len = strlen (s);
int optional = 0;
if (len > 9 && !strcmp (s + len - 9, " optional")) {
  optional = ca_optional;
  len -= 9;
}

int ok = 0;
#define VARIANT(name) \
  if (len == strlen (#name) && !strncmp (s, #name, len)) {\
    cmd.args[n - 5 - i] = ca_ ## name | optional; \
    ok = 1; \
  }

VARIANT (user)
VARIANT (chat)
VARIANT (secret_chat)
VARIANT (peer)
VARIANT (file_name)
VARIANT (file_name_end)
VARIANT (period)
VARIANT (number)
VARIANT (double)
VARIANT (string_end)
VARIANT (string)

#undef VARTIANT

if (!ok) {
  lua_pushboolean (L, 0);
  return 1;
}

}

const char *s = lua_tostring (L, -1);
lua_pop (L, 1);

cmd.desc = s ? tstrdup (s) : tstrdup ("no help provided");

int a1 = luaL_ref (L, LUA_REGISTRYINDEX);
int a2 = luaL_ref (L, LUA_REGISTRYINDEX);

struct lua_query_extra *e = malloc (sizeof (*e));
assert (e);
e->func = a2;
e->param = a1;

cmd.arg = e;

cmd.fun = do_interface_from_lua;

s = lua_tostring (L, -1);
lua_pop (L, 1);

cmd.name = tstrdup (s ? s : "none");

register_new_command (&cmd);

lua_pushboolean (L, 1);
return 1;
}

void lua_init (const char *file) {
if (!file) { return; }
have_file = 1;
luaState = luaL_newstate ();
luaL_openlibs (luaState);

int i = 0;
while (functions[i].name) {
my_lua_register (luaState, functions[i].name, universal_from_lua);
i ++;
}

lua_register (luaState, "postpone", postpone_from_lua);
lua_register (luaState, "safe_quit", safe_quit_from_lua);
lua_register (luaState, "register_interface_function", register_interface_from_lua);

print_start ();
int r = luaL_dofile (luaState, file);
print_end ();

if (r) {
logprintf ("lua: %s\n", lua_tostring (luaState, -1));
exit (1);
}
}

#endif

Copy link

@mrgerahambel1 mrgerahambel1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants