-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHonestAds
executable file
·181 lines (158 loc) · 9.86 KB
/
HonestAds
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env python3
import requests
import argparse
from pyfzf.pyfzf import FzfPrompt
def print_banner():
print("""
__ __ __ ___ __
/ / / /___ ____ ___ _____/ /_/ | ____/ /____
/ /_/ / __ \/ __ \/ _ \/ ___/ __/ /| |/ __ / ___/
/ __ / /_/ / / / / __(__ ) /_/ ___ / /_/ (__ )
/_/ /_/\____/_/ /_/\___/____/\__/_/ |_\__,_/____/
By @amocsub & @chatGPT
""")
def get_nested_value(d, keys):
for key in keys:
if isinstance(d, dict) and key in d:
d = d[key]
else:
return ""
return d
class HonestAds:
def __init__(self):
self.base_url = "https://adstransparency.google.com/"
self.session = requests.Session()
self.session.headers = {"Content-Type": "application/x-www-form-urlencoded"}
self.session.params = {"authuser":""}
def get_advertiser_by_id(self, advertiser_id):
path = "anji/_/rpc/LookupService/GetAdvertiserById"
data={"f.req": '{"1":"%s"}' % advertiser_id}
response = self.session.post(self.base_url + path, data=data)
result = response.json()
return dict(
advertiser_id=get_nested_value(result, ["1", "1"]),
advertiser_disclosed_name=get_nested_value(result, ["1", "2"]),
region_code=get_nested_value(result, ["1", "3"]),
advertiser_legal_name=get_nested_value(result, ["1", "9", "1"]),
)
def get_creative_by_id(self, advertiser_id, creative_id):
path = "anji/_/rpc/LookupService/GetCreativeById"
data={"f.req": '{"1":"%s","2":"%s","5":{"1":0,"2":0,"3":0}}' % (advertiser_id, creative_id)}
response = self.session.post(self.base_url + path, data=data)
result = response.json()
return dict(
advertiser_id=get_nested_value(result, ["1", "1"]),
creative_id=get_nested_value(result, ["1", "2"]),
first_shown=get_nested_value(result, ["1", "3", "1"]),
last_shown_timestamp=get_nested_value(result, ["1", "4", "1"]),
ad_data=[get_nested_value(ad, ["3", "2"]).split('\'')[1] if "3" in ad else get_nested_value(ad, ["1", "4"]) for ad in get_nested_value(result, ["1", "5"])],
url="https://adstransparency.google.com/advertiser/%s/creative/%s" % (get_nested_value(result, ["1", "1"]), get_nested_value(result, ["1", "2"]))
)
def search_creatives_by_text(self, word, next_page=None):
path = "anji/_/rpc/SearchService/SearchCreatives"
if next_page:
data={"f.req": '{"2":100,"3":{"12":{"1":"%s","2":true}},"4":"%s","7":{"1":1,"2":0,"3":0}}' % (word, next_page)}
else:
data={"f.req": '{"2":100,"3":{"12":{"1":"%s","2":true}},"7":{"1":1,"2":0,"3":0}}' % word}
response = self.session.post(self.base_url + path, data=data)
result = response.json()
return [dict(
advertiser_id=get_nested_value(res, ["1"]),
creative_id=get_nested_value(res, ["2"]),
advertiser_name=get_nested_value(res, ["12"]),
domain=get_nested_value(res, ["14"]),
first_shown=get_nested_value(res, ["6", "1"]),
last_shown_timestamp=get_nested_value(res, ["7", "1"]),
ad_data=get_nested_value(res, ["3", "3", "2"]).split('"')[1] if "3" in get_nested_value(res, ["3"]) else get_nested_value(res, ["3", "1", "4"]),
url="https://adstransparency.google.com/advertiser/%s/creative/%s" % (get_nested_value(res, ["1"]), get_nested_value(res, ["2"]))
) for res in get_nested_value(result, ["1"])] + self.search_creatives_by_text(word, get_nested_value(result, ["2"])) if "2" in result else []
def search_suggestions(self, text):
path = "anji/_/rpc/SearchService/SearchSuggestions"
data={"f.req": '{"1":"%s","2":10,"3":10}' % (text)}
response = self.session.post(self.base_url + path, data=data)
result = response.json()
return [{"display":get_nested_value(res, ["2", "1"]), "value":get_nested_value(res, ["2", "1"])} if "2" in res else {"display": get_nested_value(res, ["1", "1"]), "value": get_nested_value(res, ["1", "2"])} for res in get_nested_value(result, ["1"])]
def main():
# Initialize the HonestAds
ha = HonestAds()
# Set up argument parsing
parser = argparse.ArgumentParser(description="HonestAds")
parser.add_argument("--no_banner", help="Do not print the banner", action="store_true")
parser.add_argument("--verbose", help="Verbose mode (print tracebacks)", action="store_true")
parser.add_argument("search_value", help="The domain / keyword to search by", nargs='?')
# Parse the command line arguments
args = parser.parse_args()
# fzf picker
fzf = FzfPrompt()
# Display the banner if not suppressed
if not args.no_banner:
print_banner()
# Prompt the user to input the search suggestion they would like to search and then hit enter
if args.search_value:
search_value = args.search_value
else:
search_value = input("Enter the search value: ")
# Search for suggestions using the provided keyword
suggestions = ha.search_suggestions(search_value)
# Inform the user about the selection proces
selected_suggestions_display = fzf.prompt([s["display"] for s in suggestions], "--multi --header 'Select from the suggestions the things you want to search for Ads (hit TAB for multiple selection):'")
selected_suggestions = [s for s in suggestions if s["display"] in selected_suggestions_display]
all_creatives = []
for suggestion in selected_suggestions:
creatives = ha.search_creatives_by_text(suggestion["value"])
all_creatives.extend(creatives)
# Ask if the user wants to filter by the domain where the creative is displayed or by advertiser_name, also give the option to not filter
filter_option = fzf.prompt(["Filter by domain", "Filter by advertiser_name", "No filter"], "--header 'Select the filter option you want to apply to the results:'")[0]
# Depending on the selection, display the list of domains, advertiser_names, or creatives with fzf
if filter_option == "Filter by domain":
domains = list(set([creative["domain"] for creative in all_creatives]))
selected_domains = fzf.prompt(domains, "--multi --header 'Select the domains to filter by (hit TAB for multiple selection):'")
filtered_creatives = [creative for creative in all_creatives if creative["domain"] in selected_domains]
elif filter_option == "Filter by advertiser_name":
advertiser_names = list(set([creative["advertiser_name"] for creative in all_creatives]))
selected_advertiser_names = fzf.prompt(advertiser_names, "--multi --header 'Select the advertiser names to filter by (hit TAB for multiple selection):'")
filtered_creatives = [creative for creative in all_creatives if creative["advertiser_name"] in selected_advertiser_names]
else:
filtered_creatives = all_creatives
# Ask if the user wants to exclude any domains or advertiser names
exclude_option = fzf.prompt(["Exclude by domain", "Exclude by advertiser_name", "No exclusion"], "--header 'Select the option for excluding values from the results:'")[0]
if exclude_option == "Exclude by domain":
domains = list(set([creative["domain"] for creative in filtered_creatives]))
excluded_domains = fzf.prompt(domains, "--multi --header 'Select the domains to exclude (hit TAB for multiple selection):'")
filtered_creatives = [creative for creative in filtered_creatives if creative["domain"] not in excluded_domains]
elif exclude_option == "Exclude by advertiser_name":
advertiser_names = list(set([creative["advertiser_name"] for creative in filtered_creatives]))
excluded_advertiser_names = fzf.prompt(advertiser_names, "--multi --header 'Select the advertiser names to exclude (hit TAB for multiple selection):'")
filtered_creatives = [creative for creative in filtered_creatives if creative["advertiser_name"] not in excluded_advertiser_names]
# Ask the user if they want all the information or just any of the keywords, they need to select this with fzf
info_option = fzf.prompt(["All information", "Keywords only"], "--header 'Select the information option:'")[0]
if info_option == "Keywords only":
# Ask the user to select which keywords they want to display
keys = ["advertiser_id", "creative_id", "advertiser_name", "domain", "first_shown", "last_shown_timestamp", "ad_data", "url"]
selected_keys = fzf.prompt(keys, "--multi --header 'Select the keywords to display (hit TAB for multiple selection):'")
# Display the information to the user
if info_option == "All information":
lines = [f"Advertiser: {c['advertiser_name']}, Domain: {c['domain']}, URL: {c['url']}" for c in filtered_creatives]
selected_lines = fzf.prompt(lines, "--multi --header 'Filter or select specific creatives to view in detail:'")
selected_creatives = []
for line in selected_lines:
for c in filtered_creatives:
if line == f"Advertiser: {c['advertiser_name']}, Domain: {c['domain']}, URL: {c['url']}":
selected_creatives.append(c)
break
resultants = fzf.prompt(selected_creatives, "--multi --header 'Select the results you want to display as json objects (hit TAB for multiple selection):'")
else:
resultants = fzf.prompt([{k: creative[k] for k in selected_keys} for creative in filtered_creatives], "--multi --header 'Select the results you want to display as json objects (hit TAB for multiple selection):'")
for result in resultants:
print(result)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Exiting due to user interruption.")
except Exception as e:
import sys
if "--verbose" in sys.argv:
raise
else:
print(f"An error occurred: {e}")