sms.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import unicodedata
  2. import subprocess
  3. import time
  4. def strip_accents(s):
  5. return "".join(
  6. c for c in unicodedata.normalize("NFD", s) if unicodedata.category(c) != "Mn"
  7. )
  8. GSM_7_ALPHABET = (
  9. "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>"
  10. "?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑÜ`¿abcdefghijklmnopqrstuvwxyzäöñüà"
  11. )
  12. def is_gms_7(text: str) -> str:
  13. """Function that verify if the string is compatible with gsm 7 encoding"""
  14. for c in text:
  15. if not c in GSM_7_ALPHABET:
  16. return False
  17. return True
  18. def to_gsm_7(text: str) -> str:
  19. """Function that ensure the string is compatible with gsm 7 encoding"""
  20. return "".join([c if c in GSM_7_ALPHABET else strip_accents(c) for c in text])
  21. def get_index_of_last_space_before_nth_char(text, n):
  22. """Function that checks where to cut the message."""
  23. fragment = text[:n][::-1]
  24. # Split in on line feed
  25. if "\n" in fragment:
  26. return n - fragment.index("\n")
  27. # Split it on cariage return
  28. if "\r" in fragment:
  29. return n - fragment.index("\r")
  30. # Split it on white space
  31. if " " in fragment:
  32. return n - fragment.index(" ")
  33. # Split it anywhere
  34. return n
  35. def get_string_messages(msg, add_pagination=False):
  36. """Function that takes in a string message and returns
  37. an array of string messages with pagination if needed."""
  38. if is_gms_7(msg):
  39. SMS_MAX_LENGTH = 160
  40. SMS_CHUNK_LENGHT = 153
  41. else:
  42. SMS_MAX_LENGTH = 70
  43. SMS_CHUNK_LENGHT = 67
  44. if len(msg) <= SMS_MAX_LENGTH:
  45. # No pagination is needed
  46. return [msg]
  47. else:
  48. # Pagination is needed
  49. string_messages = []
  50. while msg:
  51. if len(msg) <= SMS_CHUNK_LENGHT:
  52. # if it's the last message then don't bother about not breaking words
  53. msg_len = len(msg)
  54. else:
  55. # check where is the last space before end of the sms to avoid breaking words
  56. msg_len = get_index_of_last_space_before_nth_char(msg, SMS_CHUNK_LENGHT)
  57. # generate new SMS
  58. msg_content = msg[:msg_len].strip()
  59. # append it to returned list
  60. string_messages.append(msg_content)
  61. # cut it out of the whole text
  62. msg = msg[msg_len:]
  63. # append pagination to each string message
  64. if add_pagination:
  65. for i in range(0, len(string_messages)):
  66. string_messages[i] += f"({i+1}/{len(string_messages)})"
  67. return string_messages
  68. # Android 9
  69. # adb shell service call isms 7 i32 0 s16 "com.android.mms.service" s16 "+1234567890" s16 "null" s16 "Hey\ you\ !" s16 "null" s16 "null"
  70. def send_sms(phone_number, content):
  71. number = phone_number.replace(" ", "")
  72. if type(content) == list:
  73. for msg in content:
  74. send_sms(phone_number, msg)
  75. else:
  76. message = content.replace("\n", "${NL}")
  77. command = (
  78. """adb shell "NL=$'\\n' ; service call isms 7 i32 0 s16 'com.android.mms.service' s16 \\\""""
  79. + number
  80. + "\\\" s16 'null' s16 \\\""
  81. + message
  82. + "\\\" s16 'null' s16 'null'\""
  83. )
  84. res = subprocess.check_output(command)
  85. if "Result: Parcel(00000000 '....')" in res.decode("utf-8"):
  86. return True
  87. else:
  88. raise Exception("Send SMS failed")
  89. def prepare_sms(phone_number: str, message: str, to_gsm=False):
  90. """Function that prepare the message to be send by SMS
  91. Convert it to GSM7 alphabet if required
  92. """
  93. msg = message
  94. if to_gsm:
  95. msg = to_gsm_7(msg)
  96. content = get_string_messages(msg)
  97. send_sms(phone_number, content)