Package: anysd
AnySDAnySD (Any USSD) is a package with classes to help you quickly write ussd (or ussd-like) applications.Understanding Anysd ussdAnysdAnysd usesanytreeto build a tree navigation,
andredisfor tracking navigation and session data (variables)Therefore, you need redis to continueussdsUssd applications have 2 main componentsA Form- for taking input from the userNavigation- How to reach the beginning of a form, by selecting optionsGetting startedInstall anysd in virtual environmentCreate and activate a virtualenv, then
Installanysdif you have not:>>> mkdir anysdtest && cd anysdtest

>>> virtualenv venv
created virtual environment CPython3.8.10.final.0-64 in 4591ms
  creator CPython3Posix(dest=/home/steven/workspace/anysdtest/venv, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/steven/.local/share/virtualenv)
    added seed packages: pip==22.0.4, setuptools==62.1.0, wheel==0.37.1
  activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

>>> source venv/bin/activate
(venv) >>>
(venv) >>> pip install anysd

...

(venv) >>> pip install FlaskBuilding the ussdTo build a ussd application, we first need to define the menus, first a home screen (or starting point),
then the other menus descending from the home menuIn this example, we want a ussd to buy airtime and bundles. Then for each selection, we'll need to get the phone number
of the receiver. For airtime, we need amount, and for bundles, we'll select a bundle optionstep 1: Build the questions# validator.pyfromanysdimportFormFlow,ListInputairtime_questions={"1":{'name':'AIRTIME_RECEIVER','menu':ListInput(items=['Buy for myself','Buy for other number'],title='Select Option')},'2':{'name':'AIRTIME_AMOUNT','menu':'Enter amount'},'3':{'name':'CONFIRMATION','menu':'You are about to buy{amount}airtime for{receiver}\n1. Confirm\n0. Cancel'}}bundle_packages=['100mb @15 valid 24 hours','1GB @40 valid 24 hours','5GB @100 valid 7 days']bundle_questions={"1":{'name':'BUNDLE_RECEIVER','menu':ListInput(items=['Buy for myself','Buy for other number'],title='Select Option')},'2':{'name':'BUNDLE_PACKAGE','menu':ListInput(items=bundle_packages,title='Select package')},'3':{'name':'CONFIRMATION','menu':'You are about to buy{package}for{receiver}\n1. Confirm\n0. Cancel'}}step 2: create form validatorsBy default, list inputs will be validated, but it's good you write another validator.
In validation, you specify what conditions make a user input invalid, and return either true or false on the user input.
Also, you can modify the input, if you need to
Note: the validators should accept extrakwargsthat may be passed# validator.pydefairtime_validator(current_step,last_input:str,**kwargs):valid=Truevalidated=None# in case we want to modify user input,ifcurrent_step==2:ifnotlast_input.isnumeric():# checking if amount is a numeric valuevalid=Falseelifint(last_input)<5:# checking if is less than minimumvalid=Falseelifcurrent_step==3:iflast_inputnotin['1','0']:valid=Falsereturnvalid,validateddefbundle_validator(current_step,last_input:str,**kwargs):valid=Truevalidated=None# in case we want to modify user input,ifcurrent_step==3:iflast_inputnotin['1','0']:valid=Falsereturnvalid,validatedNote: Without step validators, all inputs except for List inputs(so far), will be assumed to be validstep 3: Build the navigationLink the menu, with the forms# menu.pyfromanysdimportNavigationMenu,FormFlowfromvalidatorimport*# formsairtime_form=FormFlow(form_questions=airtime_questions,step_validator=airtime_validator)bundle_form=FormFlow(form_questions=bundle_questions,step_validator=bundle_validator)# menushome=NavigationMenu(name="Home",title="Main Menu",show_title=True)buy_airtime=NavigationMenu(name="airtime_home",title="Buy Airtime",parent=home,next_form=airtime_form)buy_bundles=NavigationMenu(name="bundles_home",title="Buy Bundles",parent=home,next_form=bundle_form)step 4: Navigation controllerTheNavigationControllerobject will be used to bind things together. It takes inmsisdn,session_idandussd_stringplus your navigationhome, then responds
with an appropriate response.We will call this inside a simple flask application.If you haven't, install flask:pip install flaskin your virtualenvfromflaskimportFlask,requestfromanysdimportNavigationControllerfrommenuimporthomeapp=Flask(__name__)@app.get('/ussd')defussd_app():msisdn=request.args.get('msisdn')session_id=request.args.get('session_id')ussd_string=request.args.get('ussd_string')print(f"{msisdn}::{session_id}:::{ussd_string}")navigator=NavigationController(home,msisdn,session_id,ussd_string)msg=navigator.navigate()returnmsgif__name__=='__main__':app.run()BEFORE WE RUN OUR BEAUTIFUL USSD, Anysd uses redis to store session data. We therefore need to specify the connection to redis in a config.yaml file# config.yamlredis:host:localhostport:6379db:4Now we are ready to run the application:(venv) >>> flask run 
* Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
2022-05-27 11:52:03,350 INFO   _log (on line 224 ) :  * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)Our flask application will receiveGETrequests in this format:http://host:port/ussd?msisdn=XXXXXXXXXXXX&session_id=XXXXXXXXXXXXX&ussd_string=XXX*XXX*XXXNow Let's use postman to hit the endpointNow let's select option 1 and see what happens:Congratulations. you have built a basic ussd application, with one level of navigation and a form.
We'll make more lessons on how to use anysd.This is a new project, so many features are going to be added, progressively