File: message.py

package info (click to toggle)
python-aprslib 0.7.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 352 kB
  • sloc: python: 2,973; makefile: 216
file content (134 lines) | stat: -rw-r--r-- 4,681 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import re
from aprslib.parsing import logger
from aprslib.parsing.telemetry import parse_telemetry_config

__all__ = [
        'parse_message',
        ]

# MESSAGE PACKET
#
# :ADDRESSEE:Message text ........{XXXXX    Up to 5 char line number
# :ADDRESSEE:ackXXXXX                       Ack for same line number
# :ADDRESSEE:Message text ........{MM}AA    Line# with REPLY ACK
#
# TELEMETRY MESSAGES
#
# :N3MIM:PARM.Battery,BTemp,AirTemp,Pres,Altude,Camra,Chute,Sun,10m,ATV
# :N3MIM:UNIT.Volts,deg.F,deg.F,Mbar,Kfeet,Clik,OPEN!,on,on,high
# :N3MIM:EQNS.0,2.6,0,0,.53,-32,3,4.39,49,-32,3,18,1,2,3
# :N3MIM:BITS.10110101,PROJECT TITLE...
def parse_message(body):
    parsed = {}

    # the while loop is used to easily break out once a match is found
    while True:
        # try to match bulletin
        match = re.findall(r"^BLN([0-9])([a-z0-9_ \-]{5}):(.{0,67})", body, re.I)
        if match:
            bid, identifier, text = match[0]
            identifier = identifier.rstrip(' ')

            mformat = 'bulletin' if identifier == "" else 'group-bulletin'

            parsed.update({
                'format': mformat,
                'message_text': text.strip(' '),
                'bid': bid,
                'identifier': identifier
                })
            break

        # try to match announcement
        match = re.findall(r"^BLN([A-Z])([a-zA-Z0-9_ \-]{5}):(.{0,67})", body)
        if match:
            aid, identifier, text = match[0]
            identifier = identifier.rstrip(' ')

            parsed.update({
                'format': 'announcement',
                'message_text': text.strip(' '),
                'aid': aid,
                'identifier': identifier
                })
            break

        # validate addresse
        match = re.findall(r"^([a-zA-Z0-9_ \-]{9}):(.*)$", body)
        if not match:
            break

        addresse, body = match[0]

        parsed.update({'addresse': addresse.rstrip(' ')})

        # check if it's a telemetry configuration message
        body, result = parse_telemetry_config(body)
        if result:
            parsed.update(result)
            break

        # regular message
        # ---------------------------
        logger.debug("Packet is just a regular message")
        parsed.update({'format': 'message'})

        # APRS supports two different message formats:
        # - the standard format which is described in 'aprs101.pdf':
        #   http://www.aprs.org/doc/APRS101.PDF
        # - an addendum from 1999 which introduces a new format:
        #   http://www.aprs.org/aprs11/replyacks.txt
        #
        # A message (ack/rej as well as a standard msg text body) can either have:
        # - no message number at all
        # - a message number in the old format (1..5 characters / digits)
        # - a message number in the new format (2 characters / digits) without trailing 'ack msg no'
        # - a message number in the new format with trailing 'free ack msg no' (2 characters / digits)

        # ack / rej
        # ---------------------------
        # NEW REPLAY-ACK
        # format: :AAAABBBBC:ackMM}AA
        match = re.findall(r"^(ack|rej)([A-Za-z0-9]{2})}([A-Za-z0-9]{2})?$", body)
        if match:
            parsed['response'], parsed['msgNo'], ackMsgNo = match[0]
            if ackMsgNo:
                parsed['ackMsgNo'] = ackMsgNo
            break

        # ack/rej standard format as per aprs101.pdf chapter 14
        # format: :AAAABBBBC:ack12345
        match = re.findall(r"^(ack|rej)([A-Za-z0-9]{1,5})$", body)
        if match:
            parsed['response'], parsed['msgNo'] = match[0]
            break

        # regular message body parser
        # ---------------------------
        parsed['message_text'] = body.strip(' ')

        # check for ACKs
        # new message format: http://www.aprs.org/aprs11/replyacks.txt
        # format: :AAAABBBBC:text.....{MM}AA
        match = re.findall(r"{([A-Za-z0-9]{2})}([A-Za-z0-9]{2})?$", body)
        if match:
            msgNo, ackMsgNo = match[0]
            parsed['message_text'] = body[:len(body) - 4 - len(ackMsgNo)].strip(' ')
            parsed['msgNo'] = msgNo
            if ackMsgNo:
                parsed['ackMsgNo'] = ackMsgNo
            break

        # old message format - see aprs101.pdf.
        # search for: msgNo present
        match = re.findall(r"{([A-Za-z0-9]{1,5})$", body)
        if match:
            msgNo = match[0]
            parsed['message_text'] = body[:len(body) - 1 - len(msgNo)].strip(' ')
            parsed['msgNo'] = msgNo
            break

        # break free from the eternal 'while'
        break

    return ('', parsed)