text: #include <stdio.h>
  #include <stdlib.h>
  #include <string.h> /* strcmp() */
  #include <signal.h>
  #include <pthread.h>
  #include <assert.h>
  
  #include "discord.h"
  
  struct discord *client;
  
  struct user_cxt {
      u64_snowflake_t channel_id;
      unsigned long long counter;
  };
  
  void
  on_ready(struct discord *client)
  {
      const struct discord_user *bot = discord_get_self(client);
  
      log_info("Succesfully connected to Discord as %s#%s!", bot->username,
               bot->discriminator);
  }
  
  void
  disconnect(struct discord *client,
             void *data,
             const struct discord_message *msg)
  {
      (void)data;
      (void)msg;
      discord_shutdown(client);
  }
  
  void
  reconnect(struct discord *client,
            void *data,
            const struct discord_message *msg)
  {
      (void)data;
      (void)msg;
      discord_reconnect(client, true);
  }
  
  void
  on_disconnect(struct discord *client, const struct discord_message *msg)
  {
      if (msg->author->bot) return;
  
      discord_create_message(client, msg->channel_id,
                             &(struct discord_create_message){
                                 .content = "Disconnecting ...",
                             },
                             &(struct discord_ret_message){
                                 .done = &disconnect,
                                 .high_p = true,
                             });
  }
  
  void
  on_reconnect(struct discord *client, const struct discord_message *msg)
  {
      if (msg->author->bot) return;
  
      discord_create_message(client, msg->channel_id,
                             &(struct discord_create_message){
                                 .content = "Reconnecting ...",
                             },
                             &(struct discord_ret_message){
                                 .done = &reconnect,
                                 .high_p = true,
                             });
  }
  
  void
  on_single(struct discord *client, const struct discord_message *msg)
  {
      if (msg->author->bot) return;
  
      discord_create_message(client, msg->channel_id,
                             &(struct discord_create_message){
                                 .content = "Hello",
                             },
                             NULL);
  }
  
  void
  send_batch(struct discord *client,
             void *data,
             const struct discord_message *msg)
  {
      char text[32];
  
      for (int i = 0; i < 128; ++i) {
          snprintf(text, sizeof(text), "%d", i);
          discord_create_message(client, msg->channel_id,
                                 &(struct discord_create_message){
                                     .content = text,
                                 },
                                 NULL);
      }
  
      discord_create_message(client, msg->channel_id,
                             &(struct discord_create_message){
                                 .content = "CHECKPOINT",
                             },
                             &(struct discord_ret_message){
                                 .done = &send_batch,
                             });
  }
  
  void
  on_spam(struct discord *client, const struct discord_message *msg)
  {
      send_batch(client, NULL, msg);
  }
  
  void
  send_msg(struct discord *client, void *data, const struct discord_message *msg)
  {
      struct user_cxt *cxt = discord_get_data(client);
      char text[32];
  
      snprintf(text, sizeof(text), "%llu", cxt->counter);
  
      discord_create_message(client, msg->channel_id,
                             &(struct discord_create_message){
                                 .content = text,
                             },
                             &(struct discord_ret_message){
                                 .done = &send_msg,
                             });
  
      ++cxt->counter;
  }
  
  void
  on_spam_ordered(struct discord *client, const struct discord_message *msg)
  {
      send_msg(client, NULL, msg);
  }
  
  void
  send_err(struct discord *client, CCORDcode code, void *data)
  {
      u64_snowflake_t channel_id = *(u64_snowflake_t *)data;
  
      discord_create_message(
          client, channel_id,
          &(struct discord_create_message){
              .content = (char *)discord_strerror(code, client),
          },
          NULL);
  }
  
  void
  on_force_error(struct discord *client, const struct discord_message *msg)
  {
      const u64_snowflake_t FAUX_CHANNEL_ID = 123;
      u64_snowflake_t *channel_id = malloc(sizeof(u64_snowflake_t));
  
      memcpy(channel_id, &msg->channel_id, sizeof(u64_snowflake_t));
  
      discord_delete_channel(client, FAUX_CHANNEL_ID,
                             &(struct discord_ret_channel){
                                 .fail = &send_err,
                                 .data = channel_id,
                                 .cleanup = &free,
                             });
  }
  
  /* shutdown gracefully on SIGINT received */
  void
  sigint_handler(int signum)
  {
    (void)signum;
    log_info("SIGINT received, shutting down ...");
    discord_shutdown(client);
  }
  
  int
  main(int argc, char *argv[])
  {
      const char *config_file;
      if (argc > 1)
          config_file = argv[1];
      else
          config_file = "../config.json";
  
      signal(SIGINT, &sigint_handler);
      ccord_global_init();
  
      client = discord_config_init(config_file);
      assert(NULL != client && "Couldn't initialize client");
  
      struct user_cxt cxt = { 0 };
      discord_set_data(client, &cxt);
  
      discord_set_on_ready(client, &on_ready);
  
      discord_set_prefix(client, "!");
      discord_set_on_command(client, "disconnect", &on_disconnect);
      discord_set_on_command(client, "reconnect", &on_reconnect);
      discord_set_on_command(client, "single", &on_single);
      discord_set_on_command(client, "spam", &on_spam);
      discord_set_on_command(client, "spam-ordered", &on_spam_ordered);
      discord_set_on_command(client, "force_error", &on_force_error);
  
      discord_run(client);
  
      discord_cleanup(client);
      ccord_global_cleanup();
  }
  