123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 |
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include <math.h>
- #include "asterisk/file.h"
- #include "asterisk/channel.h"
- #include "asterisk/pbx.h"
- #include "asterisk/module.h"
- #include "asterisk/lock.h"
- #include "asterisk/app.h"
- #include "asterisk/config.h"
- #include "asterisk/config_options.h"
- #include "asterisk/say.h"
- #include "asterisk/astobj2.h"
- #include "asterisk/acl.h"
- #include "asterisk/netsock2.h"
- #include "asterisk/strings.h"
- #include "asterisk/cli.h"
- static char *app = "SkelGuessNumber";
- enum option_flags {
- OPTION_CHEAT = (1 << 0),
- OPTION_NUMGAMES = (1 << 1),
- };
- enum option_args {
- OPTION_ARG_NUMGAMES,
-
- OPTION_ARG_ARRAY_SIZE,
- };
- AST_APP_OPTIONS(app_opts,{
- AST_APP_OPTION('c', OPTION_CHEAT),
- AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES),
- });
- struct skel_global_config {
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(prompt);
- AST_STRING_FIELD(wrong);
- AST_STRING_FIELD(right);
- AST_STRING_FIELD(high);
- AST_STRING_FIELD(low);
- AST_STRING_FIELD(lose);
- );
- uint32_t num_games;
- unsigned char cheat:1;
- };
- struct skel_level_state {
- uint32_t wins;
- uint32_t losses;
- double avg_guesses;
- };
- struct skel_level {
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name);
- );
- uint32_t max_num;
- uint32_t max_guesses;
- struct skel_level_state *state;
- };
- struct skel_current_game {
- uint32_t total_games;
- uint32_t games_left;
- uint32_t cheat;
- struct skel_level *level_info;
- };
- #define LEVEL_BUCKETS 1
- struct skel_config {
- struct skel_global_config *global;
- struct ao2_container *levels;
- };
- static void *skel_config_alloc(void);
- static void *skel_level_alloc(const char *cat);
- static void *skel_level_find(struct ao2_container *tmp_container, const char *category);
- static struct aco_type global_option = {
- .type = ACO_GLOBAL,
- .name = "globals",
- .item_offset = offsetof(struct skel_config, global),
- .category_match = ACO_WHITELIST,
- .category = "^general$",
- };
- struct aco_type *global_options[] = ACO_TYPES(&global_option);
- static struct aco_type sound_option = {
- .type = ACO_GLOBAL,
- .name = "sounds",
- .item_offset = offsetof(struct skel_config, global),
- .category_match = ACO_WHITELIST,
- .category = "^sounds$",
- };
- struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
- static struct aco_type level_option = {
- .type = ACO_ITEM,
- .name = "level",
- .category_match = ACO_BLACKLIST,
- .category = "^(general|sounds)$",
- .item_alloc = skel_level_alloc,
- .item_find = skel_level_find,
- .item_offset = offsetof(struct skel_config, levels),
- };
- struct aco_type *level_options[] = ACO_TYPES(&level_option);
- struct aco_file app_skel_conf = {
- .filename = "app_skel.conf",
- .types = ACO_TYPES(&global_option, &sound_option, &level_option),
- };
- static AO2_GLOBAL_OBJ_STATIC(globals);
- static struct ao2_container *games;
- CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
- .files = ACO_FILES(&app_skel_conf),
- );
- static void skel_global_config_destructor(void *obj)
- {
- struct skel_global_config *global = obj;
- ast_string_field_free_memory(global);
- }
- static void skel_game_destructor(void *obj)
- {
- struct skel_current_game *game = obj;
- ao2_cleanup(game->level_info);
- }
- static void skel_state_destructor(void *obj)
- {
- return;
- }
- static struct skel_current_game *skel_game_alloc(struct skel_level *level)
- {
- struct skel_current_game *game;
- if (!(game = ao2_alloc(sizeof(struct skel_current_game), skel_game_destructor))) {
- return NULL;
- }
- ao2_ref(level, +1);
- game->level_info = level;
- return game;
- }
- static void skel_level_destructor(void *obj)
- {
- struct skel_level *level = obj;
- ast_string_field_free_memory(level);
- ao2_cleanup(level->state);
- }
- static int skel_level_hash(const void *obj, const int flags)
- {
- const struct skel_level *level = obj;
- const char *name = (flags & OBJ_KEY) ? obj : level->name;
- return ast_str_case_hash(name);
- }
- static int skel_level_cmp(void *obj, void *arg, int flags)
- {
- struct skel_level *one = obj, *two = arg;
- const char *match = (flags & OBJ_KEY) ? arg : two->name;
- return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
- }
- static int custom_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
- {
- struct skel_global_config *global = obj;
- if (!strcasecmp(var->name, "cheat")) {
- global->cheat = ast_true(var->value);
- } else {
- return -1;
- }
- return 0;
- }
- static void play_files_helper(struct ast_channel *chan, const char *prompts)
- {
- char *prompt, *rest = ast_strdupa(prompts);
- ast_stopstream(chan);
- while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
- ast_stopstream(chan);
- }
- }
- static int app_exec(struct ast_channel *chan, const char *data)
- {
- int win = 0;
- uint32_t guesses;
- RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
- RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
- RAII_VAR(struct skel_current_game *, game, NULL, ao2_cleanup);
- char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
- struct ast_flags flags;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(level);
- AST_APP_ARG(options);
- );
- if (!cfg) {
- ast_log(LOG_ERROR, "Couldn't access configuratino data!\n");
- return -1;
- }
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app);
- return -1;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- if (args.argc == 2) {
- ast_app_parse_options(app_opts, &flags, opts, args.options);
- }
- if (ast_strlen_zero(args.level)) {
- ast_log(LOG_ERROR, "%s requires a level argument\n", app);
- return -1;
- }
- if (!(level = ao2_find(cfg->levels, args.level, OBJ_KEY))) {
- ast_log(LOG_ERROR, "Unknown level: %s\n", args.level);
- return -1;
- }
- if (!(game = skel_game_alloc(level))) {
- return -1;
- }
- ao2_link(games, game);
-
- if (!ast_test_flag(&flags, OPTION_NUMGAMES) ||
- ast_strlen_zero(opts[OPTION_ARG_NUMGAMES]) ||
- ast_parse_arg(opts[OPTION_ARG_NUMGAMES], PARSE_UINT32, &game->total_games)) {
- game->total_games = cfg->global->num_games;
- }
- game->games_left = game->total_games;
- game->cheat = ast_test_flag(&flags, OPTION_CHEAT) || cfg->global->cheat;
- for (game->games_left = game->total_games; game->games_left; game->games_left--) {
- uint32_t num = ast_random() % level->max_num;
- ast_debug(1, "They should totally should guess %u\n", num);
-
- play_files_helper(chan, cfg->global->prompt);
- ast_say_number(chan, level->max_num, "", ast_channel_language(chan), "");
- for (guesses = 0; guesses < level->max_guesses; guesses++) {
- size_t buflen = log10(level->max_num) + 1;
- char buf[buflen];
- int guess;
- buf[buflen] = '\0';
-
- ast_readstring(chan, buf, buflen - 1, 2000, 10000, "");
- if (ast_parse_arg(buf, PARSE_INT32 | PARSE_IN_RANGE, &guess, 0, level->max_num)) {
- if (guesses < level->max_guesses - 1) {
- play_files_helper(chan, cfg->global->wrong);
- }
- continue;
- }
-
- if (guess == num && !game->cheat) {
-
- win = 1;
- play_files_helper(chan, cfg->global->right);
- guesses++;
- break;
- } else if (guess < num) {
- play_files_helper(chan, cfg->global->low);
- } else {
- play_files_helper(chan, cfg->global->high);
- }
- if (guesses < level->max_guesses - 1) {
- play_files_helper(chan, cfg->global->wrong);
- }
- }
-
- ao2_lock(level->state);
- if (win) {
- ++level->state->wins;
- level->state->avg_guesses = ((level->state->wins - 1) * level->state->avg_guesses + guesses) / level->state->wins;
- } else {
-
- level->state->losses++;
- play_files_helper(chan, cfg->global->lose);
- }
- ao2_unlock(level->state);
- }
- ao2_unlink(games, game);
- return 0;
- }
- static struct skel_level *skel_state_alloc(const char *name)
- {
- struct skel_level *level;
- if (!(level = ao2_alloc(sizeof(*level), skel_state_destructor))) {
- return NULL;
- }
- return level;
- }
- static void *skel_level_find(struct ao2_container *tmp_container, const char *category)
- {
- return ao2_find(tmp_container, category, OBJ_KEY);
- }
- static void *skel_find_or_create_state(const char *category)
- {
- RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
- RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
- if (!cfg || !cfg->levels || !(level = ao2_find(cfg->levels, category, OBJ_KEY))) {
- return skel_state_alloc(category);
- }
- ao2_ref(level->state, +1);
- return level->state;
- }
- static void *skel_level_alloc(const char *cat)
- {
- struct skel_level *level;
- if (!(level = ao2_alloc(sizeof(*level), skel_level_destructor))) {
- return NULL;
- }
- if (ast_string_field_init(level, 128)) {
- ao2_ref(level, -1);
- return NULL;
- }
-
- if (!(level->state = skel_find_or_create_state(cat))) {
- ao2_ref(level, -1);
- return NULL;
- }
- ast_string_field_set(level, name, cat);
- return level;
- }
- static void skel_config_destructor(void *obj)
- {
- struct skel_config *cfg = obj;
- ao2_cleanup(cfg->global);
- ao2_cleanup(cfg->levels);
- }
- static void *skel_config_alloc(void)
- {
- struct skel_config *cfg;
- if (!(cfg = ao2_alloc(sizeof(*cfg), skel_config_destructor))) {
- return NULL;
- }
-
- if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), skel_global_config_destructor))) {
- goto error;
- }
- if (ast_string_field_init(cfg->global, 128)) {
- goto error;
- }
- if (!(cfg->levels = ao2_container_alloc(LEVEL_BUCKETS, skel_level_hash, skel_level_cmp))) {
- goto error;
- }
- return cfg;
- error:
- ao2_ref(cfg, -1);
- return NULL;
- }
- static char *handle_skel_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
- switch(cmd) {
- case CLI_INIT:
- e->command = "skel show config";
- e->usage =
- "Usage: skel show config\n"
- " List app_skel global config\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->global) {
- return NULL;
- }
- ast_cli(a->fd, "games per call: %u\n", cfg->global->num_games);
- ast_cli(a->fd, "computer cheats: %s\n", AST_CLI_YESNO(cfg->global->cheat));
- ast_cli(a->fd, "\n");
- ast_cli(a->fd, "Sounds\n");
- ast_cli(a->fd, " prompt: %s\n", cfg->global->prompt);
- ast_cli(a->fd, " wrong guess: %s\n", cfg->global->wrong);
- ast_cli(a->fd, " right guess: %s\n", cfg->global->right);
- return CLI_SUCCESS;
- }
- static char *handle_skel_show_games(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct ao2_iterator iter;
- struct skel_current_game *game;
- switch(cmd) {
- case CLI_INIT:
- e->command = "skel show games";
- e->usage =
- "Usage: skel show games\n"
- " List app_skel active games\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- #define SKEL_FORMAT "%-15.15s %-15.15s %-15.15s\n"
- #define SKEL_FORMAT1 "%-15.15s %-15u %-15u\n"
- ast_cli(a->fd, SKEL_FORMAT, "Level", "Total Games", "Games Left");
- iter = ao2_iterator_init(games, 0);
- while ((game = ao2_iterator_next(&iter))) {
- ast_cli(a->fd, SKEL_FORMAT1, game->level_info->name, game->total_games, game->games_left);
- ao2_ref(game, -1);
- }
- ao2_iterator_destroy(&iter);
- #undef SKEL_FORMAT
- #undef SKEL_FORMAT1
- return CLI_SUCCESS;
- }
- static char *handle_skel_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
- struct ao2_iterator iter;
- struct skel_level *level;
- switch(cmd) {
- case CLI_INIT:
- e->command = "skel show levels";
- e->usage =
- "Usage: skel show levels\n"
- " List the app_skel levels\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->levels) {
- return NULL;
- }
- #define SKEL_FORMAT "%-15.15s %-11.11s %-12.12s %-8.8s %-8.8s %-12.12s\n"
- #define SKEL_FORMAT1 "%-15.15s %-11u %-12u %-8u %-8u %-8f\n"
- ast_cli(a->fd, SKEL_FORMAT, "Name", "Max number", "Max Guesses", "Wins", "Losses", "Avg Guesses");
- iter = ao2_iterator_init(cfg->levels, 0);
- while ((level = ao2_iterator_next(&iter))) {
- ast_cli(a->fd, SKEL_FORMAT1, level->name, level->max_num, level->max_guesses, level->state->wins, level->state->losses, level->state->avg_guesses);
- ao2_ref(level, -1);
- }
- ao2_iterator_destroy(&iter);
- #undef SKEL_FORMAT
- #undef SKEL_FORMAT1
- return CLI_SUCCESS;
- }
- static struct ast_cli_entry skel_cli[] = {
- AST_CLI_DEFINE(handle_skel_show_config, "Show app_skel global config options"),
- AST_CLI_DEFINE(handle_skel_show_levels, "Show app_skel levels"),
- AST_CLI_DEFINE(handle_skel_show_games, "Show app_skel active games"),
- };
- static int reload_module(void)
- {
- if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
- return AST_MODULE_LOAD_DECLINE;
- }
- return 0;
- }
- static int unload_module(void)
- {
- ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli));
- aco_info_destroy(&cfg_info);
- ao2_global_obj_release(globals);
- ao2_cleanup(games);
- return ast_unregister_application(app);
- }
- static int load_module(void)
- {
- if (aco_info_init(&cfg_info)) {
- goto error;
- }
- if (!(games = ao2_container_alloc(1, NULL, NULL))) {
- goto error;
- }
-
- aco_option_register(&cfg_info, "games", ACO_EXACT, global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
- aco_option_register_custom(&cfg_info, "cheat", ACO_EXACT, global_options, "no", custom_bitfield_handler, 0);
-
- aco_option_register(&cfg_info, "prompt", ACO_EXACT, sound_options, "please-enter-your&number&queue-less-than", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, prompt));
- aco_option_register(&cfg_info, "wrong_guess", ACO_EXACT, sound_options, "vm-pls-try-again", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, wrong));
- aco_option_register(&cfg_info, "right_guess", ACO_EXACT, sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
- aco_option_register(&cfg_info, "too_high", ACO_EXACT, sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
- aco_option_register(&cfg_info, "too_low", ACO_EXACT, sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
- aco_option_register(&cfg_info, "lose", ACO_EXACT, sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
-
- aco_option_register(&cfg_info, "max_number", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
- aco_option_register(&cfg_info, "max_guesses", ACO_EXACT, level_options, NULL, OPT_UINT_T, 1, FLDSET(struct skel_level, max_guesses));
- if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
- goto error;
- }
- ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
- if (ast_register_application_xml(app, app_exec)) {
- goto error;
- }
- return AST_MODULE_LOAD_SUCCESS;
- error:
- aco_info_destroy(&cfg_info);
- ao2_cleanup(games);
- return AST_MODULE_LOAD_DECLINE;
- }
- AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skeleton (sample) Application",
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_DEFAULT,
- );
|