Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions scripts/artifacts/sbbmobile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
__artifacts_v2__ = {
"cff_searched_places": {
"name": "SBB Mobile - Searched places",
"description": "List of places searched in the past with last time used",
"author": "[email protected]",
"creation_date": "2026-03-26",
"last_update_date": "2026-03-26",
"requirements": "none",
"category": "Travel",
"notes": "",
"paths": ('*/data/ch.sbb.mobile.*/databases/SbbMobile.db*'),
"output_types": "standard",
"html_columns": ['location of places (link)'],
"artifact_icon": "search"
},
"cff_search_history": {
"name": "SBB Mobile - Search History",
"description": "List of all search made on application",
"author": "[email protected]",
"creation_date": "2026-03-26",
"last_update_date": "2026-03-26",
"requirements": "none",
"category": "Travel",
"notes": "",
"paths": ('*/data/ch.sbb.mobile.*/databases/SbbMobile.db*'),
"output_types": "standard",
"html_columns": ['location of search (link)'],
"artifact_icon": "search"
},
"cff_travel_cards": {
"name": "SBB Mobile - Travel Cards",
"description": "Information about public transportation pass linked to application",
"author": "[email protected]",
"creation_date": "2026-03-26",
"last_update_date": "2026-03-26",
"requirements": "none",
"category": "Travel",
"notes": "",
"paths": ('*/data/ch.sbb.mobile.*/databases/SbbMobile.db*'),
"output_types": "standard",
"artifact_icon": "user"
},
"cff_purchased_tickets": {
"name": "SBB Mobile - Ticket Purchased recently",
"description": "List of purchased ticket up to 7 days",
"author": "[email protected]",
"creation_date": "2026-03-26",
"last_update_date": "2026-03-26",
"requirements": "none",
"category": "Travel",
"notes": "",
"paths": ('*/data/ch.sbb.mobile.*/databases/SbbMobile.db*'),
"output_types": "standard",
"artifact_icon": "star"
}
}

from scripts.ilapfuncs import artifact_processor, get_file_path, \
get_sqlite_db_records, logfunc

@artifact_processor
def cff_purchased_tickets(files_found, _report_folder, _seeker, _wrap_text):
source_path = get_file_path(files_found, "SbbMobile.db")

if source_path:
query = '''
SELECT
validFrom,
validUntil,
traveler,
CASE
WHEN refundState == "NORMAL" THEN "Not Refunded"
WHEN refundState == "COMPLETE" THEN "Refunded"
ELSE refundState
END AS refundState,
paymentMethodType,
displayInfo_ticketType,
displayInfo_titleLine_firstSegment,
displayInfo_titleLine_lastSegment
FROM
PurchasedTickets
'''

data_headers = ("Valid from", "Valid until", "Traveler", "is Refunded", "Payment method", "Ticket description", "Ticket departure", "Ticket destination")
db_records = get_sqlite_db_records(source_path, query)

return data_headers, db_records, source_path
else:
logfunc('No Data')

@artifact_processor
def cff_searched_places(files_found, _report_folder, _seeker, _wrap_text):
source_path = get_file_path(files_found, "SbbMobile.db")
data_list = []

if source_path:
query = '''
SELECT
datetime(timestamp/1000, 'unixepoch', 'localtime'),
title,
CASE
WHEN favorite THEN "True"
ELSE "False"
END AS favorite,
CASE
WHEN type == "a" THEN "Address"
WHEN type == "p" THEN "POI"
WHEN type == "c" THEN "Coordinate"
WHEN type == "s" THEN "Station"
ELSE type
END AS type,
latitude,
longitude
FROM
SearchedPlaces
'''

data_headers = (("Searched timestamp", "datetime"), "Title", "Is favorite", "Type", "location of places (link)")
db_records = get_sqlite_db_records(source_path, query)

data_list = [
record[:4] + (coordinate_to_osm(record[4], record[5]),)
for record in db_records
]
return data_headers, data_list, source_path
else:
logfunc('No Data')

@artifact_processor
def cff_search_history(files_found, _report_folder, _seeker, _wrap_text):
source_path = get_file_path(files_found, "SbbMobile.db")
data_list = []

if source_path:
query = '''
SELECT
datetime(timestamp/1000, 'unixepoch', 'localtime'),
departure,
CASE
WHEN departureType == "a" THEN "Address"
WHEN departureType == "p" THEN "POI"
WHEN departureType == "c" THEN "Coordinate"
WHEN departureType == "s" THEN "Station"
ELSE departureType
END AS departureType,
target,
CASE
WHEN targetType == "a" THEN "Address"
WHEN targetType == "p" THEN "POI"
WHEN targetType == "c" THEN "Coordinate"
WHEN targetType == "s" THEN "Station"
ELSE targetType
END AS targetType,
latitude,
longitude
FROM
SearchHistory
'''

data_headers = (("Search timestamp", "datetime"), "Departure", "Departure (type)", "Destination", "Destination (type)", "location of search (link)")
db_records = get_sqlite_db_records(source_path, query)

data_list = [
record[:5] + ((coordinate_to_osm(record[5], record[6]),) if record[5] and record[6] else ("",))
for record in db_records
]

return data_headers, data_list, source_path
else:
logfunc('No Data')

@artifact_processor
def cff_travel_cards(files_found, _report_folder, _seeker, _wrap_text):
source_path = get_file_path(files_found, "SbbMobile.db")
data_list = []

if source_path:
query = '''
SELECT
name,
type,
contract_id,
valid_from,
valid_to,
contract_state
FROM
SwissPassTravelCards
'''

data_headers = ("Name", "Type", "Contract ID", "Valid From", "Valid To", "Contract state")
db_records = get_sqlite_db_records(source_path, query)

data_list = [record[:6] for record in db_records]
return data_headers, data_list, source_path
else:
logfunc('No Data')

def coordinate_to_osm(lat, lon):
return f"https://www.openstreetmap.org/?mlat={lat}&mlon={lon}&zoom=15"
Loading