2024-08-26 14:53:07 +02:00
package ussd
import (
"bytes"
"context"
"fmt"
2024-08-27 15:16:15 +02:00
"regexp"
2024-08-27 13:57:26 +02:00
"strconv"
2024-08-27 15:02:24 +02:00
"strings"
2024-08-26 14:53:07 +02:00
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/lang"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state"
"git.grassecon.net/urdt/ussd/internal/handlers/server"
2024-08-27 09:16:25 +02:00
"git.grassecon.net/urdt/ussd/internal/models"
2024-08-26 14:53:07 +02:00
"git.grassecon.net/urdt/ussd/internal/utils"
)
type FSData struct {
Path string
St * state . State
}
type Handlers struct {
2024-08-27 10:19:49 +02:00
fs * FSData
accountFileHandler * utils . AccountFileHandler
2024-08-26 14:53:07 +02:00
}
func NewHandlers ( path string , st * state . State ) * Handlers {
return & Handlers {
fs : & FSData {
Path : path ,
St : st ,
} ,
2024-08-27 10:19:49 +02:00
accountFileHandler : utils . NewAccountFileHandler ( path + "_data" ) ,
2024-08-26 14:53:07 +02:00
}
}
2024-08-28 15:23:52 +02:00
// SetLanguage sets the language across the menu
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SetLanguage ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
inputStr := string ( input )
res := resource . Result { }
switch inputStr {
case "0" :
res . FlagSet = [ ] uint32 { state . FLAG_LANG }
res . Content = "eng"
case "1" :
res . FlagSet = [ ] uint32 { state . FLAG_LANG }
res . Content = "swa"
default :
}
res . FlagSet = append ( res . FlagSet , models . USERFLAG_LANGUAGE_SET )
return res , nil
}
2024-08-28 15:23:52 +02:00
// CreateAccount checks if any account exists on the JSON data file, and if not
// creates an account on the API,
// sets the default values and flags
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) CreateAccount ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
err := h . accountFileHandler . EnsureFileExists ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 10:19:49 +02:00
2024-08-27 14:41:53 +02:00
// if an account exists, set the flag and return
existingAccountData , err := h . accountFileHandler . ReadAccountData ( )
if existingAccountData != nil {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_ACCOUNT_CREATED )
return res , err
}
2024-08-26 14:53:07 +02:00
accountResp , err := server . CreateAccount ( )
if err != nil {
2024-08-27 12:38:08 +02:00
res . FlagSet = append ( res . FlagSet , models . USERFLAG_ACCOUNT_CREATION_FAILED )
2024-08-26 14:53:07 +02:00
return res , err
}
accountData := map [ string ] string {
"TrackingId" : accountResp . Result . TrackingId ,
"PublicKey" : accountResp . Result . PublicKey ,
"CustodialId" : accountResp . Result . CustodialId . String ( ) ,
"Status" : "PENDING" ,
2024-08-27 12:30:00 +02:00
"Gender" : "Not provided" ,
"YOB" : "Not provided" ,
"Location" : "Not provided" ,
"Offerings" : "Not provided" ,
"FirstName" : "Not provided" ,
"FamilyName" : "Not provided" ,
2024-08-26 14:53:07 +02:00
}
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
res . FlagSet = append ( res . FlagSet , models . USERFLAG_ACCOUNT_CREATED )
return res , err
}
2024-08-27 12:30:00 +02:00
// SavePin persists the user's PIN choice into the filesystem
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SavePin ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
accountPIN := string ( input )
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 23:17:45 +02:00
// Validate that the PIN is a 4-digit number
2024-08-28 09:10:49 +02:00
if ! isValidPIN ( accountPIN ) {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INCORRECTPIN )
return res , nil
}
2024-08-27 23:17:45 +02:00
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INCORRECTPIN )
2024-08-26 14:53:07 +02:00
accountData [ "AccountPIN" ] = accountPIN
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
return res , nil
}
2024-08-28 09:10:49 +02:00
func ( h * Handlers ) SetResetSingleEdit ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
menuOPtion := string ( input )
switch menuOPtion {
case "2" :
2024-08-28 11:19:38 +02:00
fmt . Println ( "Resetting unlock for update" )
res . FlagReset = append ( res . FlagSet , models . USERFLAG_UNLOCKFORUPDATE )
2024-08-28 09:10:49 +02:00
res . FlagSet = append ( res . FlagSet , models . USERFLAG_SINGLE_EDIT )
case "3" :
2024-08-28 11:19:38 +02:00
res . FlagReset = append ( res . FlagSet , models . USERFLAG_UNLOCKFORUPDATE )
2024-08-28 09:10:49 +02:00
res . FlagSet = append ( res . FlagSet , models . USERFLAG_SINGLE_EDIT )
case "4" :
2024-08-28 11:19:38 +02:00
res . FlagReset = append ( res . FlagSet , models . USERFLAG_UNLOCKFORUPDATE )
2024-08-28 09:10:49 +02:00
res . FlagSet = append ( res . FlagSet , models . USERFLAG_SINGLE_EDIT )
default :
res . FlagReset = append ( res . FlagReset , models . USERFLAG_SINGLE_EDIT )
}
return res , nil
}
2024-08-28 15:23:52 +02:00
// VerifyPin checks whether the confirmation PIN is similar to the account PIN
// If similar, it sets the USERFLAG_PIN_SET flag allowing the user
// to access the main menu
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) VerifyPin ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
if bytes . Equal ( input , [ ] byte ( accountData [ "AccountPIN" ] ) ) {
res . FlagSet = [ ] uint32 { models . USERFLAG_VALIDPIN }
res . FlagReset = [ ] uint32 { models . USERFLAG_PINMISMATCH }
2024-08-27 14:41:53 +02:00
res . FlagSet = append ( res . FlagSet , models . USERFLAG_PIN_SET )
2024-08-26 14:53:07 +02:00
} else {
res . FlagSet = [ ] uint32 { models . USERFLAG_PINMISMATCH }
}
return res , nil
}
2024-08-28 15:23:52 +02:00
// isValidPIN checks whether the given input is a 4 digit number
2024-08-27 23:17:45 +02:00
func isValidPIN ( pin string ) bool {
2024-08-28 09:10:49 +02:00
match , _ := regexp . MatchString ( ` ^\d { 4}$ ` , pin )
return match
2024-08-27 23:17:45 +02:00
}
2024-08-26 14:53:07 +02:00
func codeFromCtx ( ctx context . Context ) string {
var code string
engine . Logg . DebugCtxf ( ctx , "in msg" , "ctx" , ctx , "val" , code )
if ctx . Value ( "Language" ) != nil {
lang := ctx . Value ( "Language" ) . ( lang . Language )
code = lang . Code
}
return code
}
2024-08-27 12:30:00 +02:00
// SaveFirstname updates the first name in a JSON data file with the provided input.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SaveFirstname ( cxt context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
if len ( input ) > 0 {
name := string ( input )
accountData [ "FirstName" ] = name
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
2024-08-27 12:30:00 +02:00
// SaveFamilyname updates the family name in a JSON data file with the provided input.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SaveFamilyname ( cxt context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
if len ( input ) > 0 {
secondname := string ( input )
accountData [ "FamilyName" ] = secondname
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
2024-08-27 12:30:00 +02:00
// SaveYOB updates the Year of Birth(YOB) in a JSON data file with the provided input.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SaveYob ( cxt context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 10:19:49 +02:00
2024-08-26 20:58:21 +02:00
yob := string ( input )
2024-08-27 13:57:26 +02:00
if len ( yob ) == 4 {
2024-08-26 14:53:07 +02:00
yob := string ( input )
accountData [ "YOB" ] = yob
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
2024-08-27 12:30:00 +02:00
// SaveLocation updates the location in a JSON data file with the provided input.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SaveLocation ( cxt context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 10:19:49 +02:00
2024-08-26 14:53:07 +02:00
if len ( input ) > 0 {
location := string ( input )
accountData [ "Location" ] = location
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
2024-08-27 12:30:00 +02:00
// SaveGender updates the gender in a JSON data file with the provided input.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SaveGender ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 10:19:49 +02:00
2024-08-26 14:53:07 +02:00
if len ( input ) > 0 {
gender := string ( input )
switch gender {
case "1" :
gender = "Male"
case "2" :
gender = "Female"
case "3" :
gender = "Other"
}
accountData [ "Gender" ] = gender
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
2024-08-27 12:30:00 +02:00
// SaveOfferings updates the offerings(goods and services provided by the user) in a JSON data file with the provided input.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) SaveOfferings ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 10:19:49 +02:00
2024-08-26 14:53:07 +02:00
if len ( input ) > 0 {
offerings := string ( input )
accountData [ "Offerings" ] = offerings
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
func ( h * Handlers ) ResetUnlockForUpdate ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
res . FlagReset = append ( res . FlagReset , models . USERFLAG_UNLOCKFORUPDATE )
return res , nil
}
func ( h * Handlers ) ResetAccountUnlocked ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_UNLOCKED )
return res , nil
}
2024-08-28 15:23:52 +02:00
// CheckIdentifier retrieves the PublicKey from the JSON data file.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) CheckIdentifier ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
res . Content = accountData [ "PublicKey" ]
return res , nil
}
2024-08-28 11:19:38 +02:00
// Unlock attempts to unlock the next sequential nodes by verifying the provided PIN against the already set PIN.
// It sets the required flags that control the flow.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) Unlock ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
pin := string ( input )
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-28 11:19:38 +02:00
if len ( input ) == 4 {
2024-08-26 14:53:07 +02:00
if pin != accountData [ "AccountPIN" ] {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INCORRECTPIN )
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_UNLOCKED )
return res , nil
}
if h . fs . St . MatchFlag ( models . USERFLAG_ACCOUNT_UNLOCKED , false ) {
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INCORRECTPIN )
res . FlagSet = append ( res . FlagSet , models . USERFLAG_UNLOCKFORUPDATE )
res . FlagSet = append ( res . FlagSet , models . USERFLAG_ACCOUNT_UNLOCKED )
} else {
2024-08-28 11:19:38 +02:00
res . FlagSet = append ( res . FlagSet , models . USERFLAG_UNLOCKFORUPDATE )
2024-08-26 14:53:07 +02:00
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_UNLOCKED )
}
}
return res , nil
}
func ( h * Handlers ) ResetIncorrectPin ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INCORRECTPIN )
return res , nil
}
2024-08-28 15:23:52 +02:00
// CheckAccountStatus queries the API using the TrackingId and sets flags
// based on the account status
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) CheckAccountStatus ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
status , err := server . CheckAccountStatus ( accountData [ "TrackingId" ] )
if err != nil {
fmt . Println ( "Error checking account status:" , err )
return res , nil
}
accountData [ "Status" ] = status
if status == "SUCCESS" {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_ACCOUNT_SUCCESS )
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_PENDING )
} else {
res . FlagReset = append ( res . FlagSet , models . USERFLAG_ACCOUNT_SUCCESS )
res . FlagSet = append ( res . FlagReset , models . USERFLAG_ACCOUNT_PENDING )
}
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
return res , nil
}
2024-08-28 15:23:52 +02:00
// Quit displays the Thank you message and exits the menu
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) Quit ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
switch codeFromCtx ( ctx ) {
case "swa" :
res . Content = "Asante kwa kutumia huduma ya Sarafu. Kwaheri!"
default :
res . Content = "Thank you for using Sarafu. Goodbye!"
}
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_UNLOCKED )
return res , nil
}
2024-08-28 15:23:52 +02:00
// VerifyYob verifies the length of the given input
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) VerifyYob ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
date := string ( input )
2024-08-27 13:57:26 +02:00
_ , err := strconv . Atoi ( date )
2024-08-27 15:16:15 +02:00
if err != nil {
// If conversion fails, input is not numeric
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INCORRECTDATEFORMAT )
return res , nil
}
2024-08-27 13:57:26 +02:00
if len ( date ) == 4 {
2024-08-26 14:53:07 +02:00
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INCORRECTDATEFORMAT )
2024-08-27 13:57:26 +02:00
} else {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INCORRECTDATEFORMAT )
2024-08-26 14:53:07 +02:00
}
return res , nil
}
func ( h * Handlers ) ResetIncorrectYob ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INCORRECTDATEFORMAT )
return res , nil
}
2024-08-28 15:23:52 +02:00
// CheckBalance retrieves the balance from the API using the "PublicKey" and sets
// the balance as the result content
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) CheckBalance ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 10:19:49 +02:00
2024-08-26 14:53:07 +02:00
balance , err := server . CheckBalance ( accountData [ "PublicKey" ] )
if err != nil {
return res , nil
}
res . Content = balance
return res , nil
}
2024-08-28 15:23:52 +02:00
// ValidateRecipient validates that the given input is a valid phone number.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) ValidateRecipient ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
recipient := string ( input )
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
if recipient != "0" {
// mimic invalid number check
if recipient == "000" {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INVALID_RECIPIENT )
res . Content = recipient
return res , nil
}
accountData [ "Recipient" ] = recipient
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
}
return res , nil
}
2024-08-28 15:23:52 +02:00
// TransactionReset resets the previous transaction data (Recipient and Amount)
// as well as the invalid flags
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) TransactionReset ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-28 13:11:20 +02:00
// reset the transaction
2024-08-26 14:53:07 +02:00
accountData [ "Recipient" ] = ""
2024-08-28 13:11:20 +02:00
accountData [ "Amount" ] = ""
2024-08-26 14:53:07 +02:00
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INVALID_RECIPIENT , models . USERFLAG_INVALID_RECIPIENT_WITH_INVITE )
return res , nil
}
2024-08-28 15:23:52 +02:00
// ResetTransactionAmount resets the transaction amount and invalid flag
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) ResetTransactionAmount ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
// reset the amount
accountData [ "Amount" ] = ""
2024-08-27 10:19:49 +02:00
err = h . accountFileHandler . WriteAccountData ( accountData )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
res . FlagReset = append ( res . FlagReset , models . USERFLAG_INVALID_AMOUNT )
return res , nil
}
2024-08-28 15:23:52 +02:00
// MaxAmount gets the current balance from the API and sets it as
// the result content.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) MaxAmount ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 15:10:43 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
if err != nil {
return res , err
}
balance , err := server . CheckBalance ( accountData [ "PublicKey" ] )
if err != nil {
return res , nil
}
res . Content = balance
2024-08-26 14:53:07 +02:00
return res , nil
}
2024-08-28 15:23:52 +02:00
// ValidateAmount ensures that the given input is a valid amount and that
// it is not more than the current balance.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) ValidateAmount ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
2024-08-27 15:16:15 +02:00
res := resource . Result { }
amountStr := string ( input )
accountData , err := h . accountFileHandler . ReadAccountData ( )
if err != nil {
return res , err
}
balanceStr , err := server . CheckBalance ( accountData [ "PublicKey" ] )
if err != nil {
return res , err
}
res . Content = balanceStr
// Parse the balance
balanceParts := strings . Split ( balanceStr , " " )
if len ( balanceParts ) != 2 {
return res , fmt . Errorf ( "unexpected balance format: %s" , balanceStr )
}
balanceValue , err := strconv . ParseFloat ( balanceParts [ 0 ] , 64 )
if err != nil {
return res , fmt . Errorf ( "failed to parse balance: %v" , err )
}
2024-08-26 14:53:07 +02:00
2024-08-27 15:16:15 +02:00
// Extract numeric part from input
re := regexp . MustCompile ( ` ^(\d+(\.\d+)?)\s*(?:CELO)?$ ` )
matches := re . FindStringSubmatch ( strings . TrimSpace ( amountStr ) )
if len ( matches ) < 2 {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INVALID_AMOUNT )
res . Content = amountStr
return res , nil
}
inputAmount , err := strconv . ParseFloat ( matches [ 1 ] , 64 )
if err != nil {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INVALID_AMOUNT )
res . Content = amountStr
return res , nil
}
if inputAmount > balanceValue {
res . FlagSet = append ( res . FlagSet , models . USERFLAG_INVALID_AMOUNT )
res . Content = amountStr
return res , nil
}
res . Content = fmt . Sprintf ( "%.3f" , inputAmount ) // Format to 3 decimal places
accountData [ "Amount" ] = res . Content
err = h . accountFileHandler . WriteAccountData ( accountData )
if err != nil {
return res , err
}
return res , nil
}
2024-08-28 15:23:52 +02:00
// GetRecipient returns the transaction recipient from a JSON data file.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) GetRecipient ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
res . Content = accountData [ "Recipient" ]
return res , nil
}
2024-08-27 10:22:37 +02:00
// GetProfileInfo retrieves and formats the profile information of a user from a JSON data file.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) GetProfileInfo ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 12:30:00 +02:00
var age string
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
2024-08-27 12:30:00 +02:00
var name string
if accountData [ "FirstName" ] == "Not provided" || accountData [ "FamilyName" ] == "Not provided" {
name = "Not provided"
} else {
name = accountData [ "FirstName" ] + " " + accountData [ "FamilyName" ]
}
2024-08-26 14:53:07 +02:00
gender := accountData [ "Gender" ]
yob := accountData [ "YOB" ]
location := accountData [ "Location" ]
offerings := accountData [ "Offerings" ]
2024-08-27 12:30:00 +02:00
if yob == "Not provided" {
age = "Not provided"
} else {
2024-08-27 13:57:26 +02:00
ageInt , err := strconv . Atoi ( yob )
2024-08-27 12:30:00 +02:00
if err != nil {
return res , nil
}
2024-08-27 13:57:26 +02:00
age = strconv . Itoa ( utils . CalculateAgeWithYOB ( ageInt ) )
2024-08-26 14:53:07 +02:00
}
2024-08-27 12:30:00 +02:00
formattedData := fmt . Sprintf ( "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n" , name , gender , age , location , offerings )
2024-08-26 14:53:07 +02:00
res . Content = formattedData
return res , nil
}
2024-08-27 10:22:37 +02:00
// GetSender retrieves the public key from a JSON data file.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) GetSender ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
res . Content = accountData [ "PublicKey" ]
return res , nil
}
2024-08-28 13:11:20 +02:00
// GetAmount retrieves the amount from a JSON data file.
func ( h * Handlers ) GetAmount ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
accountData , err := h . accountFileHandler . ReadAccountData ( )
if err != nil {
return res , err
}
res . Content = accountData [ "Amount" ]
return res , nil
}
2024-08-28 11:19:38 +02:00
// QuickWithBalance retrieves the balance for a given public key from the custodial balance API endpoint before
// gracefully exiting the session.
2024-08-26 14:53:07 +02:00
func ( h * Handlers ) QuitWithBalance ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-27 10:19:49 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
2024-08-26 14:53:07 +02:00
if err != nil {
return res , err
}
balance , err := server . CheckBalance ( accountData [ "PublicKey" ] )
if err != nil {
return res , nil
}
res . Content = fmt . Sprintf ( "Your account balance is: %s" , balance )
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_UNLOCKED )
return res , nil
}
2024-08-28 13:11:20 +02:00
2024-08-28 15:23:52 +02:00
// InitiateTransaction returns a confirmation and resets the transaction data
// on the JSON file.
2024-08-28 13:11:20 +02:00
func ( h * Handlers ) InitiateTransaction ( ctx context . Context , sym string , input [ ] byte ) ( resource . Result , error ) {
res := resource . Result { }
2024-08-28 13:54:39 +02:00
accountData , err := h . accountFileHandler . ReadAccountData ( )
if err != nil {
return res , err
}
2024-08-28 13:11:20 +02:00
// TODO
// Use the amount, recipient and sender to call the API and initialize the transaction
2024-08-28 13:54:39 +02:00
switch codeFromCtx ( ctx ) {
case "swa" :
res . Content = fmt . Sprintf ( "Ombi lako limetumwa. %s atapokea %s kutoka kwa %s." , accountData [ "Recipient" ] , accountData [ "Amount" ] , accountData [ "PublicKey" ] )
default :
res . Content = fmt . Sprintf ( "Your request has been sent. %s will receive %s from %s." , accountData [ "Recipient" ] , accountData [ "Amount" ] , accountData [ "PublicKey" ] )
}
// reset the transaction
accountData [ "Recipient" ] = ""
accountData [ "Amount" ] = ""
err = h . accountFileHandler . WriteAccountData ( accountData )
if err != nil {
return res , err
}
2024-08-28 13:11:20 +02:00
res . FlagReset = append ( res . FlagReset , models . USERFLAG_ACCOUNT_UNLOCKED )
return res , nil
}