text: #include "discord/member.h"
  #include "discord/private/_discord.h"
  #include "discord/private/_api.h"
  #include "discord/private/_json.h"
  #include "cutils.h"
  #include "estr.h"
  
  DISCORD_LOG_DEFINE_BASE();
  
  esp_err_t discord_member_get(discord_handle_t client, char* guild_id, char* user_id, discord_member_t** out_member) {
      if(! client || ! guild_id || ! user_id || ! out_member) {
          DISCORD_LOGE("Invalid args");
          return ESP_ERR_INVALID_ARG;
      }
  
      esp_err_t err = ESP_OK;
      discord_member_t* member = NULL;
      discord_api_response_t* res = NULL;
      
      if((err = dcapi_get(
          client,
          estr_cat("/guilds/", guild_id, "/members/", user_id),
          NULL,
          true,
          &res
      )) != ESP_OK) {
          return err;
      }
  
      if(dcapi_response_is_success(res) && res->data_len > 0) {
          member = discord_json_deserialize_(member, res->data, res->data_len);
      }
  
      dcapi_response_free(client, res);
  
      *out_member = member;
      return err;
  }
  
  static bool dc_member_permissions_calc(discord_handle_t client, discord_member_t* member, discord_role_t** roles, discord_role_len_t roles_len, uint64_t permissions) {
      discord_role_sort_list(roles, roles_len);
  
      uint64_t o_ring = strtoull(roles[0]->permissions, NULL, 10); // @everyone permissions
  
      if((o_ring & DISCORD_PERMISSION_ADMINISTRATOR) == DISCORD_PERMISSION_ADMINISTRATOR) { // @everyone has administrator
          return true;
      }
  
      bool in_ids = false;
      if(member->_roles_len > 0) {
          for(discord_role_len_t i = 1; i < roles_len; i++) {
              discord_role_is_in_ids_list(roles[i], member->roles, member->_roles_len, &in_ids);
              
              if(! in_ids) { // member does not have this role
                  continue;
              }
  
              o_ring |= strtoull(roles[i]->permissions, NULL, 10);
  
              if((o_ring & DISCORD_PERMISSION_ADMINISTRATOR) == DISCORD_PERMISSION_ADMINISTRATOR) {
                  return true;
              }
          }
      }
      
      return (o_ring & permissions) == permissions;
  }
  
  esp_err_t discord_member_has_permissions(discord_handle_t client, discord_member_t* member, char* guild_id, uint64_t permissions, bool* out_result) {
      if(! client || ! member || ! guild_id || ! out_result) {
          DISCORD_LOGE("Invalid args");
          return ESP_ERR_INVALID_ARG;
      }
  
      discord_role_len_t len;
      discord_role_t** roles = NULL;
      esp_err_t err = discord_role_get_all(client, guild_id, &roles, &len);
  
      if(err != ESP_OK) {
          return err;
      }
      
      bool res = dc_member_permissions_calc(client, member, roles, len, permissions);
      cu_list_tfreex(roles, discord_role_len_t, len, discord_role_free);
  
      *out_result = res;
      return ESP_OK;
  }
  
  esp_err_t discord_member_has_role_name(discord_handle_t client, discord_member_t* member, const char* guild_id, const char* role_name, bool* out_result) {
      if(! client || ! member || ! guild_id || ! role_name || ! out_result) {
          DISCORD_LOGE("Invalid args");
          return ESP_ERR_INVALID_ARG;
      }
  
      discord_role_len_t len;
      discord_role_t** roles = NULL;
      
      esp_err_t err = discord_role_get_all(client, guild_id, &roles, &len);
  
      if(err != ESP_OK) {
          return err;
      }
  
      discord_role_t* required_role = NULL;
  
      for(discord_role_len_t i = 0; i < len; i++) {
          if(estr_eq(roles[i]->name, role_name)) {
              required_role = roles[i];
              break;
          }
      }
  
      if(required_role == NULL) { // role doesn't exist in guild
          cu_list_tfreex(roles, discord_role_len_t, len, discord_role_free);
          *out_result = false;
          return ESP_OK;
      }
  
      // role exist in guild, check if role is assigned to member
      bool result = false;
      for(discord_role_len_t i = 0; i < member->_roles_len; i++) {
          if(estr_eq(required_role->id, member->roles[i])) {
              result = true;
              break;
          }
      }
  
      *out_result = result;
      return ESP_OK;
  }
  
  void discord_member_free(discord_member_t* member) {
      if(!member)
          return;
  
      free(member->nick);
      free(member->permissions);
      cu_list_free(member->roles, member->_roles_len);
      free(member);
  }