Coverage for src/couchers/phone/sms.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-07-20 21:46 +0000

1import logging 

2 

3import boto3 

4import luhn 

5 

6from couchers import crypto 

7from couchers.config import config 

8from couchers.db import session_scope 

9from couchers.models import SMS 

10 

11logger = logging.getLogger(__name__) 

12 

13 

14def generate_random_code(): 

15 """Return a random 6-digit string with correct Luhn checksum""" 

16 return luhn.append(crypto.generate_random_5digit_string()) 

17 

18 

19def looks_like_a_code(string): 

20 return len(string) == 6 and string.isdigit() and luhn.verify(string) 

21 

22 

23def format_message(token): 

24 return f"{token} is your Couchers.org verification code. If you did not request this, please ignore this message. Best, the Couchers.org team." 

25 

26 

27def send_sms(number, message): 

28 """Send SMS to a E.164 formatted phone number with leading +. Return "success" on 

29 success, "unsupported operator" on unsupported operator, and any other 

30 string for any other error.""" 

31 

32 assert len(message) <= 140, "Message too long" 

33 

34 if not config["ENABLE_SMS"]: 

35 logger.info(f"SMS not enabled, need to send to {number}: {message}") 

36 return "SMS not enabled." 

37 

38 sns = boto3.client("sns") 

39 sender_id = config["SMS_SENDER_ID"] 

40 

41 response = sns.publish( 

42 PhoneNumber=number, 

43 Message=message, 

44 MessageAttributes={ 

45 "AWS.SNS.SMS.SMSType": {"DataType": "String", "StringValue": "Transactional"}, 

46 "AWS.SNS.SMS.SenderID": {"DataType": "String", "StringValue": sender_id}, 

47 }, 

48 ) 

49 

50 message_id = response["MessageId"] 

51 

52 with session_scope() as session: 

53 session.add( 

54 SMS( 

55 message_id=message_id, 

56 number=number, 

57 message=message, 

58 sms_sender_id=sender_id, 

59 ) 

60 ) 

61 

62 return "success"