| 1 | #define PURPLE_PLUGINS |
| 2 | #define PREF_PREFIX "/plugins/gtk/autoanswer" |
| 3 | #define PREF_MODE PREF_PREFIX "/use_funny_mode" |
| 4 | #define PREF_LIMIT PREF_PREFIX "/limit_of_answers" |
| 5 | #define PREF_ROBOT PREF_PREFIX "/unhide_robot" |
| 6 | #define PREF_SEARCH PREF_PREFIX "/use_all_logs" |
| 7 | #define PREF_DICT PREF_PREFIX "/use_dct" |
| 8 | #define PREF_VERBOSE PREF_PREFIX "/use_verbose_mode" |
| 9 | #define PREF_PROMPT PREF_PREFIX "/robots_prompt" |
| 10 | |
| 11 | #define _(String) ((const char *) (String)) |
| 12 | |
| 13 | #include <gtk/gtk.h> |
| 14 | |
| 15 | /* Purple headers */ |
| 16 | #include <libpurple/debug.h> |
| 17 | #include <libpurple/version.h> |
| 18 | #include <libpurple/conversation.h> |
| 19 | #include <libpurple/debug.h> |
| 20 | #include <libpurple/log.h> |
| 21 | #include <libpurple/plugin.h> |
| 22 | #include <libpurple/pluginpref.h> |
| 23 | #include <libpurple/prefs.h> |
| 24 | #include <libpurple/signals.h> |
| 25 | #include <libpurple/util.h> |
| 26 | #include <libpurple/notify.h> |
| 27 | |
| 28 | /* Pidgin headers */ |
| 29 | #include <pidgin/gtkaccount.h> |
| 30 | #include <pidgin/gtkblist.h> |
| 31 | #include <pidgin/gtkconv.h> |
| 32 | #include <pidgin/gtkimhtml.h> |
| 33 | #include <pidgin/gtkplugin.h> |
| 34 | #include <pidgin/gtkprefs.h> |
| 35 | #include <pidgin/gtkutils.h> |
| 36 | #include <pidgin/pidgin.h> |
| 37 | |
| 38 | |
| 39 | |
| 40 | #include <time.h> |
| 41 | #include <string.h> |
| 42 | #include <stdio.h> |
| 43 | #include <sys/stat.h> |
| 44 | #include <ftw.h> |
| 45 | #include <glib.h> |
| 46 | |
| 47 | |
| 48 | char *buff = NULL; |
| 49 | char *buddyname1 = NULL; |
| 50 | char *buddyname1_alias = NULL; |
| 51 | char *username_alias = NULL; |
| 52 | char *username = NULL; |
| 53 | GList *answers = NULL; |
| 54 | int limit_of_answers = 10; |
| 55 | |
| 56 | int list(const char *, const struct stat *, int); |
| 57 | static void create_send_combo_pidgin(PidginConversation *gtkconv); |
| 58 | |
| 59 | gint compare_str(gconstpointer a, gconstpointer b) |
| 60 | { |
| 61 | if (a==NULL) return 1; |
| 62 | if (b==NULL) return -1; |
| 63 | return strcmp(a,b); |
| 64 | } |
| 65 | |
| 66 | int list (const char *name, const struct stat *status, int type) { |
| 67 | |
| 68 | GList *files = NULL; |
| 69 | FILE *curlog; |
| 70 | char curline[1024]; |
| 71 | char *match = NULL; |
| 72 | char answer[1024], *answer_body, *tmp_ptr; |
| 73 | char *me; //, *me_end; |
| 74 | int me_length; |
| 75 | int current_number_of_answers = g_list_length(answers); |
| 76 | |
| 77 | if (type == FTW_NS) return 0; |
| 78 | if (type == FTW_F) { |
| 79 | /* purple_debug_misc("auto-answer-history DEBUG", "%-30s\t0%3o\n", name, status->st_mode&0777); */ |
| 80 | files = g_list_append(files, (gpointer) name); |
| 81 | } |
| 82 | /* else { |
| 83 | purple_debug_misc("auto-answer-history DEBUG", "Logdir is: %-30s*\t0%3o\n", name, status->st_mode&0777); |
| 84 | purple_debug_misc("auto-answer-history DEBUG", "Going to find \"%s\" from buddy %s (%s)\n", buff, buddyname1, buddyname1_alias); |
| 85 | }*/ |
| 86 | |
| 87 | if (username_alias != NULL) me = username_alias; |
| 88 | else me = username; |
| 89 | |
| 90 | me_length=strlen(me); |
| 91 | |
| 92 | while(files) { |
| 93 | /* purple_debug_misc("auto-answer-history DEBUG", "Processing... %s\n", files->data); */ |
| 94 | if ((curlog = fopen(files->data, "r")) != NULL) { |
| 95 | while (fgets(curline, sizeof curline, curlog) != NULL) { |
| 96 | if ( ((strstr(curline, buff)) != NULL) && ((strstr(curline, me)) == NULL)) { |
| 97 | match = strstr(curline, buff); |
| 98 | /* purple_debug_misc("auto-answer-history DEBUG", "FOUND in %s, line %d, looking for answer...\n", files->data, counter); */ |
| 99 | while (fgets(answer, sizeof answer, curlog) !=NULL ) { |
| 100 | if ((strstr(answer, me)) != NULL) { |
| 101 | if (purple_markup_strip_html(answer) != NULL) { |
| 102 | if (current_number_of_answers>limit_of_answers) |
| 103 | { fclose(curlog); |
| 104 | return 0; |
| 105 | } |
| 106 | /* check if such answer isn't already in a GList */ |
| 107 | |
| 108 | answer_body=purple_markup_strip_html(answer); |
| 109 | tmp_ptr=strchr(answer_body,':')+1; tmp_ptr=strchr(tmp_ptr,':')+1; |
| 110 | tmp_ptr=strchr(tmp_ptr,':')+1; /*skip 3 semi-colons in the preface*/ |
| 111 | |
| 112 | answer_body=strstr(answer_body,me)+me_length+1; /*skip preface*/ |
| 113 | if (tmp_ptr > answer_body) answer_body = tmp_ptr; /* choose the better method of preface skip*/ |
| 114 | |
| 115 | if (!g_list_find_custom(answers,answer_body,compare_str)) |
| 116 | { g_list_append (answers, answer_body); |
| 117 | current_number_of_answers++; |
| 118 | } |
| 119 | |
| 120 | } |
| 121 | break; |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | fclose(curlog); |
| 128 | files = files->next; |
| 129 | } |
| 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | static void |
| 134 | send_combo_cb(GtkWidget *widget, PidginConversation *gtkconv) |
| 135 | { /* A button to send item from combobox was pressed */ |
| 136 | GtkIMHtmlOptions options; |
| 137 | GtkWidget *send_combo; |
| 138 | gchar *text, *pref_prompt; |
| 139 | gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); |
| 140 | /* if (purple_prefs_get_bool(PREF_ROBOT)) |
| 141 | {*/ /*gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), _("The possible answer is:<B>"), options);*/ |
| 142 | pref_prompt=purple_prefs_get_string(PREF_PROMPT); |
| 143 | if (strlen(pref_prompt)>0) |
| 144 | { gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), pref_prompt, options); |
| 145 | gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), "<B>", options); |
| 146 | } |
| 147 | // purple_debug_misc("auto-answer-history DEBUG", "the prompt was \"%d\"", strlen(pref_prompt)); |
| 148 | |
| 149 | /* }*/ |
| 150 | send_combo = g_object_get_data(G_OBJECT(gtkconv->toolbar), "send_combo"); |
| 151 | text=gtk_combo_box_get_active_text(GTK_COMBO_BOX(send_combo)); |
| 152 | gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), text, options); |
| 153 | g_free(text); |
| 154 | g_signal_emit_by_name(gtkconv->entry, "message_send"); |
| 155 | } |
| 156 | |
| 157 | static void |
| 158 | received_im_msg_cb(PurpleAccount *account, char *who, char *buffer, |
| 159 | PurpleConversation *conv, PurpleMessageFlags flags, void *data) |
| 160 | { |
| 161 | const char *name; |
| 162 | char* logdir; |
| 163 | char* foundmessage = NULL; |
| 164 | gchar * pref_prompt; |
| 165 | int number_of_lines; |
| 166 | |
| 167 | GtkWidget *send_combo = NULL; |
| 168 | PurpleBuddy *b; |
| 169 | GtkIMHtmlOptions options; |
| 170 | PidginConversation *gtkconv = NULL; |
| 171 | |
| 172 | |
| 173 | limit_of_answers=purple_prefs_get_int(PREF_LIMIT); |
| 174 | |
| 175 | /* A workaround to avoid skipping of the first message as a result on NULL-conv: */ |
| 176 | if (conv == NULL) conv=purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who); |
| 177 | |
| 178 | |
| 179 | |
| 180 | buff = purple_markup_strip_html(buffer); |
| 181 | printf("\nHarvie received 1: %s\n", buffer); |
| 182 | purple_conv_im_send(purple_conversation_get_im_data(conv), ":-*"); |
| 183 | |
| 184 | purple_debug_info("auto-answer-history DEBUG", "buff: %s\n", buff); |
| 185 | username = purple_account_get_username(account); |
| 186 | purple_debug_info("auto-answer-history DEBUG", "username: %s\n", username); |
| 187 | username_alias = purple_account_get_alias(account); |
| 188 | buddyname1 = purple_conversation_get_name(conv); /** this shoud work fine, at least with ICQ **/ |
| 189 | purple_debug_info("auto-answer-history DEBUG", "buddyname: %s\n", buddyname1); |
| 190 | b = purple_find_buddy(account, buddyname1); |
| 191 | buddyname1_alias = purple_buddy_get_alias_only(b); |
| 192 | |
| 193 | name = purple_conversation_get_name(conv); |
| 194 | logdir = purple_log_get_log_dir(PURPLE_LOG_IM, name, account); |
| 195 | /* purple_debug_info("auto-answer-history DEBUG", "LogDir: %s\n", logdir); */ |
| 196 | |
| 197 | answers=g_list_alloc(); |
| 198 | ftw(logdir, list, 1); |
| 199 | purple_debug_misc("auto-answer-history DEBUG", "Number of answers found is: %d\n", (g_list_length(answers)-1)); |
| 200 | |
| 201 | if (g_list_length(answers) == 1) return; /* exit if no possible answers found */ |
| 202 | |
| 203 | gtkconv = PIDGIN_CONVERSATION(conv); |
| 204 | |
| 205 | if (purple_prefs_get_bool(PREF_VERBOSE)) gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<HR>", options); |
| 206 | |
| 207 | send_combo = g_object_get_data(G_OBJECT(gtkconv->toolbar), "send_combo"); |
| 208 | if (send_combo == NULL) { |
| 209 | create_send_combo_pidgin(gtkconv); |
| 210 | send_combo = g_object_get_data(G_OBJECT(gtkconv->toolbar), "send_combo"); |
| 211 | } |
| 212 | else { /* clear combo box */ |
| 213 | number_of_lines = gtk_tree_model_iter_n_children(gtk_combo_box_get_model(send_combo), NULL); |
| 214 | for(; number_of_lines; number_of_lines--) gtk_combo_box_remove_text(send_combo, number_of_lines-1); |
| 215 | purple_debug_misc("auto-answer-history DEBUG", "number of lines to delete:%d\n", number_of_lines); |
| 216 | } |
| 217 | |
| 218 | while (answers) { |
| 219 | if (answers->data != 0) { |
| 220 | g_strchomp(answers->data); |
| 221 | foundmessage = (char*) answers->data; |
| 222 | |
| 223 | if (purple_prefs_get_bool(PREF_VERBOSE)) /* Flood all possible answers into my conversation window */ |
| 224 | { |
| 225 | |
| 226 | pref_prompt=purple_prefs_get_string(PREF_PROMPT); |
| 227 | if (strlen(pref_prompt)>0) |
| 228 | { gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), pref_prompt, options); |
| 229 | gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<B>", options); |
| 230 | } |
| 231 | |
| 232 | |
| 233 | gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), foundmessage, options); |
| 234 | gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "</B><BR>", options); |
| 235 | } |
| 236 | /* A more flexible (than Copy/Paste) way to select answer: */ |
| 237 | gtk_combo_box_append_text(send_combo, foundmessage); |
| 238 | |
| 239 | } |
| 240 | answers = answers->next; |
| 241 | } |
| 242 | number_of_lines = gtk_tree_model_iter_n_children(gtk_combo_box_get_model(send_combo), NULL); |
| 243 | gtk_combo_box_set_active(send_combo, rand()%number_of_lines); |
| 244 | |
| 245 | |
| 246 | |
| 247 | if (purple_prefs_get_bool(PREF_VERBOSE)) gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<HR>", options); |
| 248 | |
| 249 | |
| 250 | if (purple_prefs_get_bool(PREF_MODE)) |
| 251 | { |
| 252 | |
| 253 | /* If "Funny Mode" is ON - going to autosend an answer */ |
| 254 | foundmessage=gtk_combo_box_get_active_text(send_combo); |
| 255 | |
| 256 | purple_conv_im_send(PURPLE_CONV_IM(conv), foundmessage); |
| 257 | gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); |
| 258 | g_free(foundmessage); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | static GtkWidget * |
| 263 | get_plugin_pref_frame(PurplePlugin *plugin) { |
| 264 | |
| 265 | GtkWidget *vbox = NULL; |
| 266 | GtkWidget *frame = NULL; |
| 267 | GtkWidget *auto_option = NULL; |
| 268 | GtkWidget *limit_option = NULL; |
| 269 | GtkWidget *verbose_option = NULL; |
| 270 | GtkWidget *prompt_option = NULL; |
| 271 | |
| 272 | vbox = gtk_vbox_new(TRUE, PIDGIN_HIG_BOX_SPACE); |
| 273 | |
| 274 | frame = pidgin_make_frame(vbox, _("Auto-answer options")); |
| 275 | limit_option = pidgin_prefs_labeled_spin_button (frame, _("Maximum answers to find"), PREF_LIMIT, 5, 100, NULL); |
| 276 | auto_option = pidgin_prefs_checkbox(_("Send automatically"), PREF_MODE, frame); |
| 277 | verbose_option = pidgin_prefs_checkbox(_("Use verbose mode"), PREF_VERBOSE, frame); |
| 278 | prompt_option = pidgin_prefs_labeled_entry(frame, _("Preface robot's answer with"), PREF_PROMPT, NULL); |
| 279 | |
| 280 | gtk_widget_show_all(vbox); |
| 281 | |
| 282 | return vbox; |
| 283 | } |
| 284 | |
| 285 | |
| 286 | static PurplePluginUiInfo ui_info = { |
| 287 | get_plugin_pref_frame, |
| 288 | 0, /* page_num (Reserved) */ |
| 289 | NULL, /* frame (Reserved) */ |
| 290 | |
| 291 | /* padding */ |
| 292 | NULL, |
| 293 | NULL, |
| 294 | NULL, |
| 295 | NULL |
| 296 | }; |
| 297 | |
| 298 | |
| 299 | static void |
| 300 | create_send_combo_pidgin(PidginConversation *gtkconv) |
| 301 | { |
| 302 | GtkWidget *send_combo, *say_button; |
| 303 | GtkRequisition greq; |
| 304 | /* purple_debug_misc("auto-answer-history DEBUG", "combo created\n");*/ |
| 305 | |
| 306 | send_combo = gtk_combo_box_new_text(); |
| 307 | gtk_combo_box_set_focus_on_click(send_combo, FALSE); |
| 308 | |
| 309 | gtk_box_pack_end(GTK_BOX(gtkconv->toolbar), send_combo, TRUE, TRUE, 0); |
| 310 | gtk_widget_show(send_combo); |
| 311 | |
| 312 | gtk_widget_size_request(send_combo, &greq); |
| 313 | gtk_widget_set_size_request (send_combo, greq.width, greq.height); |
| 314 | |
| 315 | g_object_set_data(G_OBJECT(gtkconv->toolbar), "send_combo", send_combo); |
| 316 | |
| 317 | |
| 318 | |
| 319 | say_button = gtk_button_new_with_mnemonic(_("Say")); |
| 320 | gtk_button_set_focus_on_click(say_button, FALSE); |
| 321 | gtk_button_set_relief(say_button, GTK_RELIEF_NONE); |
| 322 | g_signal_connect(G_OBJECT(say_button), "clicked", |
| 323 | G_CALLBACK(send_combo_cb), gtkconv); |
| 324 | gtk_box_pack_end(GTK_BOX(gtkconv->toolbar), say_button, FALSE, FALSE, 0); |
| 325 | gtk_widget_show(say_button); |
| 326 | |
| 327 | g_object_set_data(G_OBJECT(gtkconv->toolbar), "say_button", say_button); |
| 328 | |
| 329 | |
| 330 | } |
| 331 | |
| 332 | static void |
| 333 | remove_send_combo_pidgin(PidginConversation *gtkconv) |
| 334 | { |
| 335 | GtkWidget *send_combo = NULL, *say_button = NULL; |
| 336 | |
| 337 | send_combo = g_object_get_data(G_OBJECT(gtkconv->toolbar), "send_combo"); |
| 338 | if (send_combo != NULL) { |
| 339 | gtk_widget_destroy(send_combo); |
| 340 | } |
| 341 | |
| 342 | say_button = g_object_get_data(G_OBJECT(gtkconv->toolbar), "say_button"); |
| 343 | if (say_button != NULL) { |
| 344 | gtk_widget_destroy(say_button); |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | |
| 349 | /* Plugin Routine */ |
| 350 | |
| 351 | static gboolean |
| 352 | plugin_load(PurplePlugin *plugin) |
| 353 | { |
| 354 | void *conv_handle = purple_conversations_get_handle(); |
| 355 | |
| 356 | purple_signal_connect(conv_handle, "received-im-msg", |
| 357 | plugin, PURPLE_CALLBACK(received_im_msg_cb), NULL); |
| 358 | return TRUE; |
| 359 | } |
| 360 | |
| 361 | static gboolean |
| 362 | plugin_unload(PurplePlugin *plugin) { |
| 363 | |
| 364 | GList *convs = purple_get_conversations(); |
| 365 | |
| 366 | while (convs) { |
| 367 | PurpleConversation *conv = (PurpleConversation *)convs->data; |
| 368 | |
| 369 | /* Remove Send combo */ |
| 370 | if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { |
| 371 | remove_send_combo_pidgin(PIDGIN_CONVERSATION(conv)); |
| 372 | } |
| 373 | |
| 374 | convs = convs->next; |
| 375 | } |
| 376 | |
| 377 | return TRUE; |
| 378 | } |
| 379 | |
| 380 | |
| 381 | static PurplePluginInfo info = |
| 382 | { |
| 383 | PURPLE_PLUGIN_MAGIC, |
| 384 | PURPLE_MAJOR_VERSION, |
| 385 | PURPLE_MINOR_VERSION, |
| 386 | PURPLE_PLUGIN_STANDARD, /**< type */ |
| 387 | PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ |
| 388 | 0, /**< flags */ |
| 389 | NULL, /**< dependencies */ |
| 390 | PURPLE_PRIORITY_DEFAULT, /**< priority */ |
| 391 | |
| 392 | "pidgin-autokiss", /**< id */ |
| 393 | "AutoKiss", /**< name */ |
| 394 | "0.1", /**< version */ |
| 395 | /** summary */ |
| 396 | "Automatically answering based on regexes.", |
| 397 | /** description */ |
| 398 | "Automatically answering based on regexp", |
| 399 | "Harvie <harvie@email.cz>", /**< author */ |
| 400 | "http://sourceforge.net/projects/pidgin-autoansw", /**< homepage */ |
| 401 | |
| 402 | plugin_load, /**< load */ |
| 403 | plugin_unload, /**< unload */ |
| 404 | NULL, /**< destroy */ |
| 405 | &ui_info, /**< ui_info */ |
| 406 | NULL, /**< extra_info */ |
| 407 | NULL, /**< prefs_info */ |
| 408 | NULL, /* this tells libpurple the address of the function to call |
| 409 | to get the list of plugin actions. */ |
| 410 | |
| 411 | /* padding */ |
| 412 | NULL, |
| 413 | NULL, |
| 414 | NULL, |
| 415 | NULL |
| 416 | }; |
| 417 | |
| 418 | static void |
| 419 | init_plugin(PurplePlugin *plugin) |
| 420 | { |
| 421 | purple_prefs_add_none(PREF_PREFIX); |
| 422 | purple_prefs_add_bool(PREF_MODE, FALSE); |
| 423 | purple_prefs_add_bool(PREF_VERBOSE, FALSE); |
| 424 | purple_prefs_add_string(PREF_PROMPT, "The possible answer is:"); |
| 425 | |
| 426 | /** TODO: Add more search options and uninformative words dictionary support**/ |
| 427 | |
| 428 | purple_prefs_add_bool(PREF_SEARCH, FALSE); |
| 429 | purple_prefs_add_bool(PREF_DICT, FALSE); |
| 430 | |
| 431 | } |
| 432 | |
| 433 | PURPLE_INIT_PLUGIN(autoanswer, init_plugin, info) |