feat(gmail): new Gmail integration
Gmail API + Directory API (27 commands): mailbox search/read/send/ trash/label/move, attachments retrieval, vacation auto-reply, filters, forwarding addresses, delegates, and Workspace user administration; message ingestion (get_incidents) with a bundled OCSF mapper. Service-account auth with domain-wide delegation (JWT RS256), runs on a remote engine (requires PyJWT + cryptography). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.basic"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
criteria = {}
|
||||
if inputs.get("from"):
|
||||
criteria["from"] = inputs.get("from")
|
||||
if inputs.get("to"):
|
||||
criteria["to"] = inputs.get("to")
|
||||
if inputs.get("subject"):
|
||||
criteria["subject"] = inputs.get("subject")
|
||||
if inputs.get("query"):
|
||||
criteria["query"] = inputs.get("query")
|
||||
if inputs.get("has_attachment"):
|
||||
criteria["hasAttachment"] = True
|
||||
if not criteria:
|
||||
raise Exception("no criteria given")
|
||||
|
||||
action = {}
|
||||
add_labels = [s.strip() for s in str(inputs.get("add_labels") or "").split(",") if s.strip()]
|
||||
if add_labels:
|
||||
action["addLabelIds"] = add_labels
|
||||
remove_labels = [s.strip() for s in str(inputs.get("remove_labels") or "").split(",") if s.strip()]
|
||||
if remove_labels:
|
||||
action["removeLabelIds"] = remove_labels
|
||||
if inputs.get("forward"):
|
||||
action["forward"] = inputs.get("forward")
|
||||
|
||||
result = request("POST", base + "/settings/filters", SCOPES,
|
||||
body={"criteria": criteria, "action": action}, subject=mailbox)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,77 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.basic"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
res = request("GET", base + "/settings/vacation", SCOPES, subject=mailbox)
|
||||
print(json.dumps(res))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,112 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
from datetime import datetime
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.basic"]
|
||||
|
||||
|
||||
def _bool(value, default=False):
|
||||
if value in (None, ""):
|
||||
return default
|
||||
if isinstance(value, str):
|
||||
return value.strip().lower() in ("1", "true", "yes", "y")
|
||||
return bool(value)
|
||||
|
||||
|
||||
def _to_epoch_ms(value):
|
||||
s = str(value).strip()
|
||||
if s.isdigit():
|
||||
n = int(s)
|
||||
if n < 10**12:
|
||||
n *= 1000
|
||||
return n
|
||||
return int(datetime.fromisoformat(s.replace("Z", "+00:00")).timestamp() * 1000)
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
body = {"enableAutoReply": _bool(inputs.get("enable"), default=True)}
|
||||
if inputs.get("response_subject"):
|
||||
body["responseSubject"] = str(inputs["response_subject"])
|
||||
if inputs.get("response_body"):
|
||||
body["responseBodyPlainText"] = str(inputs["response_body"])
|
||||
if inputs.get("response_body_html"):
|
||||
body["responseBodyHtml"] = str(inputs["response_body_html"])
|
||||
if inputs.get("contacts_only") not in (None, ""):
|
||||
body["restrictToContacts"] = _bool(inputs.get("contacts_only"))
|
||||
if inputs.get("domain_only") not in (None, ""):
|
||||
body["restrictToDomain"] = _bool(inputs.get("domain_only"))
|
||||
if inputs.get("start_time") not in (None, ""):
|
||||
body["startTime"] = _to_epoch_ms(inputs["start_time"])
|
||||
if inputs.get("end_time") not in (None, ""):
|
||||
body["endTime"] = _to_epoch_ms(inputs["end_time"])
|
||||
|
||||
res = request("PUT", base + "/settings/vacation", SCOPES, body=body, subject=mailbox)
|
||||
print(json.dumps(res))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,97 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/admin.directory.user"]
|
||||
ADMIN_BASE = "https://admin.googleapis.com/admin/directory/v1"
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
email = str(inputs.get("email") or "").strip()
|
||||
first_name = str(inputs.get("first_name") or "").strip()
|
||||
last_name = str(inputs.get("last_name") or "").strip()
|
||||
password = str(inputs.get("password") or "")
|
||||
if not email:
|
||||
raise Exception("email is required")
|
||||
if not first_name:
|
||||
raise Exception("first_name is required")
|
||||
if not last_name:
|
||||
raise Exception("last_name is required")
|
||||
if len(password) < 8 or len(password) > 100:
|
||||
raise Exception("password must be between 8 and 100 characters")
|
||||
|
||||
body = {
|
||||
"primaryEmail": email,
|
||||
"name": {
|
||||
"givenName": first_name,
|
||||
"familyName": last_name,
|
||||
"fullName": first_name + " " + last_name,
|
||||
},
|
||||
"password": password,
|
||||
}
|
||||
result = request("POST", ADMIN_BASE + "/users", SCOPES, body=body)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,83 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.sharing"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
delegate_email = str(inputs.get("delegate_email") or "").strip()
|
||||
if not delegate_email:
|
||||
raise Exception("delegate_email is required")
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("POST", base + "/settings/delegates", SCOPES,
|
||||
body={"delegateEmail": delegate_email}, subject=mailbox)
|
||||
if not result:
|
||||
result = {"ok": True, "delegate_email": delegate_email}
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,84 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.sharing"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
delegate_email = str(inputs.get("delegate_email") or "").strip()
|
||||
if not delegate_email:
|
||||
raise Exception("delegate_email is required")
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("DELETE",
|
||||
base + "/settings/delegates/" + urllib.parse.quote(delegate_email, safe=""),
|
||||
SCOPES, subject=mailbox)
|
||||
if not result:
|
||||
result = {"ok": True, "delegate_email": delegate_email}
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,93 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://mail.google.com/"]
|
||||
|
||||
|
||||
def _bool(value):
|
||||
if isinstance(value, str):
|
||||
return value.strip().lower() in ("1", "true", "yes", "y")
|
||||
return bool(value)
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
message_id = str(inputs.get("message_id") or "").strip()
|
||||
if not message_id:
|
||||
raise Exception("message_id is required")
|
||||
|
||||
if _bool(inputs.get("permanent")):
|
||||
request("DELETE", base + "/messages/" + urllib.parse.quote(message_id, safe=""),
|
||||
SCOPES, subject=mailbox)
|
||||
print(json.dumps({"ok": True, "message_id": message_id}))
|
||||
else:
|
||||
res = request("POST", base + "/messages/" + urllib.parse.quote(message_id, safe="") + "/trash",
|
||||
SCOPES, body={}, subject=mailbox)
|
||||
print(json.dumps(res))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,81 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/admin.directory.user"]
|
||||
ADMIN_BASE = "https://admin.googleapis.com/admin/directory/v1"
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
user_key = str(inputs.get("user_key") or "").strip()
|
||||
if not user_key:
|
||||
raise Exception("user_key is required")
|
||||
|
||||
result = request("DELETE", ADMIN_BASE + "/users/" + urllib.parse.quote(user_key, safe=""), SCOPES)
|
||||
if not result:
|
||||
result = {"ok": True, "user_key": user_key}
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,81 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.sharing"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
forwarding_email = str(inputs.get("forwarding_email") or "").strip()
|
||||
if not forwarding_email:
|
||||
raise Exception("forwarding_email is required")
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("POST", base + "/settings/forwardingAddresses", SCOPES,
|
||||
body={"forwardingEmail": forwarding_email}, subject=mailbox)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,77 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.basic"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("GET", base + "/settings/forwardingAddresses", SCOPES, subject=mailbox)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,84 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.sharing"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
forwarding_email = str(inputs.get("forwarding_email") or "").strip()
|
||||
if not forwarding_email:
|
||||
raise Exception("forwarding_email is required")
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("DELETE",
|
||||
base + "/settings/forwardingAddresses/" + urllib.parse.quote(forwarding_email, safe=""),
|
||||
SCOPES, subject=mailbox)
|
||||
if not result:
|
||||
result = {"ok": True, "forwarding_email": forwarding_email}
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,83 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.sharing"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
forwarding_email = str(inputs.get("forwarding_email") or "").strip()
|
||||
if not forwarding_email:
|
||||
raise Exception("forwarding_email is required")
|
||||
disposition = str(inputs.get("disposition") or "leaveInInbox")
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("PUT", base + "/settings/autoForwarding", SCOPES,
|
||||
body={"emailAddress": forwarding_email, "enabled": True, "disposition": disposition},
|
||||
subject=mailbox)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,144 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
import base64
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def flatten_message(msg):
|
||||
payload = msg.get("payload", {})
|
||||
headers = {h.get("name", "").lower(): h.get("value", "") for h in payload.get("headers", [])}
|
||||
acc = {"body_text": "", "body_html": "", "attachments": []}
|
||||
|
||||
def walk(part):
|
||||
for sub in part.get("parts", []) or []:
|
||||
walk(sub)
|
||||
mime = part.get("mimeType", "")
|
||||
data = part.get("body", {}).get("data")
|
||||
if part.get("filename"):
|
||||
acc["attachments"].append({
|
||||
"id": part.get("body", {}).get("attachmentId", ""),
|
||||
"name": part.get("filename", ""),
|
||||
"mime_type": mime,
|
||||
})
|
||||
elif data:
|
||||
text = base64.urlsafe_b64decode(data.encode("ascii")).decode("utf-8", "replace")
|
||||
if mime == "text/html":
|
||||
acc["body_html"] += text
|
||||
else:
|
||||
acc["body_text"] += text
|
||||
|
||||
walk(payload)
|
||||
return {
|
||||
"id": msg.get("id"),
|
||||
"thread_id": msg.get("threadId"),
|
||||
"labels": msg.get("labelIds", []),
|
||||
"snippet": msg.get("snippet", ""),
|
||||
"internal_date": msg.get("internalDate"),
|
||||
"subject": headers.get("subject", ""),
|
||||
"from": headers.get("from", ""),
|
||||
"to": headers.get("to", ""),
|
||||
"cc": headers.get("cc", ""),
|
||||
"date": headers.get("date", ""),
|
||||
"message_id_header": headers.get("message-id", ""),
|
||||
"body_text": acc["body_text"],
|
||||
"body_html": acc["body_html"],
|
||||
"attachments": acc["attachments"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
message_id = str(inputs.get("message_id") or "").strip()
|
||||
if not message_id:
|
||||
raise Exception("message_id is required")
|
||||
|
||||
msg = request("GET", base + "/messages/" + urllib.parse.quote(message_id, safe=""),
|
||||
SCOPES, params={"format": "full"}, subject=mailbox)
|
||||
flat = flatten_message(msg)
|
||||
|
||||
attachments = []
|
||||
for att in flat["attachments"]:
|
||||
att_id = att.get("id", "")
|
||||
if not att_id:
|
||||
continue
|
||||
res = request("GET",
|
||||
base + "/messages/" + urllib.parse.quote(message_id, safe="")
|
||||
+ "/attachments/" + urllib.parse.quote(att_id, safe=""),
|
||||
SCOPES, subject=mailbox)
|
||||
data = str(res.get("data") or "")
|
||||
raw = base64.urlsafe_b64decode(data + "=" * (-len(data) % 4)) if data else b""
|
||||
attachments.append({
|
||||
"name": att.get("name", ""),
|
||||
"mime_type": att.get("mime_type", ""),
|
||||
"size": res.get("size", len(raw)),
|
||||
"content_base64": base64.b64encode(raw).decode("ascii"),
|
||||
})
|
||||
print(json.dumps({"message_id": message_id, "attachments": attachments}))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,143 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
import base64
|
||||
from datetime import datetime
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def flatten_message(msg):
|
||||
payload = msg.get("payload", {})
|
||||
headers = {h.get("name", "").lower(): h.get("value", "") for h in payload.get("headers", [])}
|
||||
acc = {"body_text": "", "body_html": "", "attachments": []}
|
||||
|
||||
def walk(part):
|
||||
for sub in part.get("parts", []) or []:
|
||||
walk(sub)
|
||||
mime = part.get("mimeType", "")
|
||||
data = part.get("body", {}).get("data")
|
||||
if part.get("filename"):
|
||||
acc["attachments"].append({
|
||||
"id": part.get("body", {}).get("attachmentId", ""),
|
||||
"name": part.get("filename", ""),
|
||||
"mime_type": mime,
|
||||
})
|
||||
elif data:
|
||||
text = base64.urlsafe_b64decode(data.encode("ascii")).decode("utf-8", "replace")
|
||||
if mime == "text/html":
|
||||
acc["body_html"] += text
|
||||
else:
|
||||
acc["body_text"] += text
|
||||
|
||||
walk(payload)
|
||||
return {
|
||||
"id": msg.get("id"),
|
||||
"thread_id": msg.get("threadId"),
|
||||
"labels": msg.get("labelIds", []),
|
||||
"snippet": msg.get("snippet", ""),
|
||||
"internal_date": msg.get("internalDate"),
|
||||
"subject": headers.get("subject", ""),
|
||||
"from": headers.get("from", ""),
|
||||
"to": headers.get("to", ""),
|
||||
"cc": headers.get("cc", ""),
|
||||
"date": headers.get("date", ""),
|
||||
"message_id_header": headers.get("message-id", ""),
|
||||
"body_text": acc["body_text"],
|
||||
"body_html": acc["body_html"],
|
||||
"attachments": acc["attachments"],
|
||||
}
|
||||
|
||||
|
||||
def _to_epoch_seconds(value):
|
||||
s = str(value).strip()
|
||||
if s.isdigit():
|
||||
n = int(s)
|
||||
if n > 10**12:
|
||||
n //= 1000
|
||||
return n
|
||||
return int(datetime.fromisoformat(s.replace("Z", "+00:00")).timestamp())
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
q = str(inputs.get("query") or "").strip()
|
||||
after = inputs.get("after")
|
||||
if after not in (None, ""):
|
||||
q = (q + " " if q else "") + "after:" + str(_to_epoch_seconds(after))
|
||||
max_results = int(inputs.get("max") or 50)
|
||||
|
||||
res = request("GET", base + "/messages", SCOPES,
|
||||
params={"q": q, "maxResults": max_results}, subject=mailbox)
|
||||
out = []
|
||||
for m in res.get("messages", []) or []:
|
||||
full = request("GET", base + "/messages/" + urllib.parse.quote(str(m.get("id", "")), safe=""),
|
||||
SCOPES, params={"format": "full"}, subject=mailbox)
|
||||
out.append(flatten_message(full))
|
||||
print(json.dumps({"result": out}))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,125 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
import base64
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def flatten_message(msg):
|
||||
payload = msg.get("payload", {})
|
||||
headers = {h.get("name", "").lower(): h.get("value", "") for h in payload.get("headers", [])}
|
||||
acc = {"body_text": "", "body_html": "", "attachments": []}
|
||||
|
||||
def walk(part):
|
||||
for sub in part.get("parts", []) or []:
|
||||
walk(sub)
|
||||
mime = part.get("mimeType", "")
|
||||
data = part.get("body", {}).get("data")
|
||||
if part.get("filename"):
|
||||
acc["attachments"].append({
|
||||
"id": part.get("body", {}).get("attachmentId", ""),
|
||||
"name": part.get("filename", ""),
|
||||
"mime_type": mime,
|
||||
})
|
||||
elif data:
|
||||
text = base64.urlsafe_b64decode(data.encode("ascii")).decode("utf-8", "replace")
|
||||
if mime == "text/html":
|
||||
acc["body_html"] += text
|
||||
else:
|
||||
acc["body_text"] += text
|
||||
|
||||
walk(payload)
|
||||
return {
|
||||
"id": msg.get("id"),
|
||||
"thread_id": msg.get("threadId"),
|
||||
"labels": msg.get("labelIds", []),
|
||||
"snippet": msg.get("snippet", ""),
|
||||
"internal_date": msg.get("internalDate"),
|
||||
"subject": headers.get("subject", ""),
|
||||
"from": headers.get("from", ""),
|
||||
"to": headers.get("to", ""),
|
||||
"cc": headers.get("cc", ""),
|
||||
"date": headers.get("date", ""),
|
||||
"message_id_header": headers.get("message-id", ""),
|
||||
"body_text": acc["body_text"],
|
||||
"body_html": acc["body_html"],
|
||||
"attachments": acc["attachments"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
message_id = str(inputs.get("message_id") or "").strip()
|
||||
if not message_id:
|
||||
raise Exception("message_id is required")
|
||||
|
||||
msg = request("GET", base + "/messages/" + urllib.parse.quote(message_id, safe=""),
|
||||
SCOPES, params={"format": "full"}, subject=mailbox)
|
||||
print(json.dumps(flatten_message(msg)))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,126 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
import base64
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def flatten_message(msg):
|
||||
payload = msg.get("payload", {})
|
||||
headers = {h.get("name", "").lower(): h.get("value", "") for h in payload.get("headers", [])}
|
||||
acc = {"body_text": "", "body_html": "", "attachments": []}
|
||||
|
||||
def walk(part):
|
||||
for sub in part.get("parts", []) or []:
|
||||
walk(sub)
|
||||
mime = part.get("mimeType", "")
|
||||
data = part.get("body", {}).get("data")
|
||||
if part.get("filename"):
|
||||
acc["attachments"].append({
|
||||
"id": part.get("body", {}).get("attachmentId", ""),
|
||||
"name": part.get("filename", ""),
|
||||
"mime_type": mime,
|
||||
})
|
||||
elif data:
|
||||
text = base64.urlsafe_b64decode(data.encode("ascii")).decode("utf-8", "replace")
|
||||
if mime == "text/html":
|
||||
acc["body_html"] += text
|
||||
else:
|
||||
acc["body_text"] += text
|
||||
|
||||
walk(payload)
|
||||
return {
|
||||
"id": msg.get("id"),
|
||||
"thread_id": msg.get("threadId"),
|
||||
"labels": msg.get("labelIds", []),
|
||||
"snippet": msg.get("snippet", ""),
|
||||
"internal_date": msg.get("internalDate"),
|
||||
"subject": headers.get("subject", ""),
|
||||
"from": headers.get("from", ""),
|
||||
"to": headers.get("to", ""),
|
||||
"cc": headers.get("cc", ""),
|
||||
"date": headers.get("date", ""),
|
||||
"message_id_header": headers.get("message-id", ""),
|
||||
"body_text": acc["body_text"],
|
||||
"body_html": acc["body_html"],
|
||||
"attachments": acc["attachments"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
thread_id = str(inputs.get("thread_id") or "").strip()
|
||||
if not thread_id:
|
||||
raise Exception("thread_id is required")
|
||||
|
||||
thread = request("GET", base + "/threads/" + urllib.parse.quote(thread_id, safe=""),
|
||||
SCOPES, params={"format": "full"}, subject=mailbox)
|
||||
messages = [flatten_message(m) for m in thread.get("messages", []) or []]
|
||||
print(json.dumps({"id": thread_id, "messages": messages}))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,79 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/admin.directory.user.readonly"]
|
||||
ADMIN_BASE = "https://admin.googleapis.com/admin/directory/v1"
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
user_key = str(inputs.get("user_key") or "").strip()
|
||||
if not user_key:
|
||||
raise Exception("user_key is required")
|
||||
|
||||
result = request("GET", ADMIN_BASE + "/users/" + urllib.parse.quote(user_key, safe=""), SCOPES)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,80 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/admin.directory.user"]
|
||||
ADMIN_BASE = "https://admin.googleapis.com/admin/directory/v1"
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
user_key = str(inputs.get("user_key") or "").strip()
|
||||
if not user_key:
|
||||
raise Exception("user_key is required")
|
||||
|
||||
result = request("PUT", ADMIN_BASE + "/users/" + urllib.parse.quote(user_key, safe=""), SCOPES,
|
||||
body={"includeInGlobalAddressList": bool(inputs.get("visible"))})
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,77 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.basic"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("GET", base + "/settings/filters", SCOPES, subject=mailbox)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,77 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
res = request("GET", base + "/labels", SCOPES, subject=mailbox)
|
||||
print(json.dumps(res))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,96 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/admin.directory.user.readonly"]
|
||||
ADMIN_BASE = "https://admin.googleapis.com/admin/directory/v1"
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
max_results = int(inputs.get("max_results") or 100)
|
||||
if max_results > 500:
|
||||
max_results = 500
|
||||
|
||||
domain = str(inputs.get("domain") or "").strip()
|
||||
customer = ""
|
||||
if not domain:
|
||||
default_user = str(_cfg().get("user_id") or "")
|
||||
if "@" in default_user:
|
||||
domain = default_user.split("@", 1)[1]
|
||||
else:
|
||||
customer = "my_customer"
|
||||
|
||||
params = {
|
||||
"domain": domain,
|
||||
"query": inputs.get("query"),
|
||||
"maxResults": max_results,
|
||||
"pageToken": inputs.get("page_token"),
|
||||
"showDeleted": "true" if inputs.get("show_deleted") else None,
|
||||
"customer": customer,
|
||||
}
|
||||
result = request("GET", ADMIN_BASE + "/users", SCOPES, params=params)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,96 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.modify"]
|
||||
|
||||
|
||||
def _csv(value):
|
||||
return [x.strip() for x in str(value or "").split(",") if x.strip()]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
message_id = str(inputs.get("message_id") or "").strip()
|
||||
if not message_id:
|
||||
raise Exception("message_id is required")
|
||||
|
||||
add_labels = _csv(inputs.get("add_labels"))
|
||||
remove_labels = _csv(inputs.get("remove_labels"))
|
||||
body = {}
|
||||
if add_labels:
|
||||
body["addLabelIds"] = add_labels
|
||||
if remove_labels:
|
||||
body["removeLabelIds"] = remove_labels
|
||||
if not body:
|
||||
raise Exception("Provide add_labels and/or remove_labels")
|
||||
|
||||
res = request("POST", base + "/messages/" + urllib.parse.quote(message_id, safe="") + "/modify",
|
||||
SCOPES, body=body, subject=mailbox)
|
||||
print(json.dumps(res))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,83 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.settings.basic"]
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
filter_id = str(inputs.get("filter_id") or "").strip()
|
||||
if not filter_id:
|
||||
raise Exception("filter_id is required")
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox)
|
||||
|
||||
result = request("DELETE", base + "/settings/filters/" + urllib.parse.quote(filter_id, safe=""),
|
||||
SCOPES, subject=mailbox)
|
||||
if not result:
|
||||
result = {"ok": True, "filter_id": filter_id}
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,143 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
import base64
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def flatten_message(msg):
|
||||
payload = msg.get("payload", {})
|
||||
headers = {h.get("name", "").lower(): h.get("value", "") for h in payload.get("headers", [])}
|
||||
acc = {"body_text": "", "body_html": "", "attachments": []}
|
||||
|
||||
def walk(part):
|
||||
for sub in part.get("parts", []) or []:
|
||||
walk(sub)
|
||||
mime = part.get("mimeType", "")
|
||||
data = part.get("body", {}).get("data")
|
||||
if part.get("filename"):
|
||||
acc["attachments"].append({
|
||||
"id": part.get("body", {}).get("attachmentId", ""),
|
||||
"name": part.get("filename", ""),
|
||||
"mime_type": mime,
|
||||
})
|
||||
elif data:
|
||||
text = base64.urlsafe_b64decode(data.encode("ascii")).decode("utf-8", "replace")
|
||||
if mime == "text/html":
|
||||
acc["body_html"] += text
|
||||
else:
|
||||
acc["body_text"] += text
|
||||
|
||||
walk(payload)
|
||||
return {
|
||||
"id": msg.get("id"),
|
||||
"thread_id": msg.get("threadId"),
|
||||
"labels": msg.get("labelIds", []),
|
||||
"snippet": msg.get("snippet", ""),
|
||||
"internal_date": msg.get("internalDate"),
|
||||
"subject": headers.get("subject", ""),
|
||||
"from": headers.get("from", ""),
|
||||
"to": headers.get("to", ""),
|
||||
"cc": headers.get("cc", ""),
|
||||
"date": headers.get("date", ""),
|
||||
"message_id_header": headers.get("message-id", ""),
|
||||
"body_text": acc["body_text"],
|
||||
"body_html": acc["body_html"],
|
||||
"attachments": acc["attachments"],
|
||||
}
|
||||
|
||||
|
||||
def _bool(value):
|
||||
if isinstance(value, str):
|
||||
return value.strip().lower() in ("1", "true", "yes", "y")
|
||||
return bool(value)
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
max_results = int(inputs.get("max_results") or 100)
|
||||
if max_results > 500:
|
||||
max_results = 500
|
||||
params = {
|
||||
"q": inputs.get("query"),
|
||||
"maxResults": max_results,
|
||||
"pageToken": inputs.get("page_token"),
|
||||
"labelIds": inputs.get("label_id"),
|
||||
}
|
||||
if inputs.get("include_spam_trash") not in (None, ""):
|
||||
params["includeSpamTrash"] = "true" if _bool(inputs.get("include_spam_trash")) else "false"
|
||||
|
||||
res = request("GET", base + "/messages", SCOPES, params=params, subject=mailbox)
|
||||
out = []
|
||||
for m in res.get("messages", []) or []:
|
||||
full = request("GET", base + "/messages/" + urllib.parse.quote(str(m.get("id", "")), safe=""),
|
||||
SCOPES, params={"format": "full"}, subject=mailbox)
|
||||
out.append(flatten_message(full))
|
||||
print(json.dumps({"result": out, "next_page_token": res.get("nextPageToken", "")}))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,133 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
import base64
|
||||
import mimetypes
|
||||
from email import encoders
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/gmail.compose",
|
||||
"https://www.googleapis.com/auth/gmail.send",
|
||||
]
|
||||
|
||||
|
||||
def _build_message(inputs, mailbox):
|
||||
body = str(inputs.get("body") or "")
|
||||
html_body = str(inputs.get("html_body") or "")
|
||||
att_name = str(inputs.get("attachment_name") or "")
|
||||
att_b64 = str(inputs.get("attachment_base64") or "")
|
||||
|
||||
if html_body or att_b64:
|
||||
message = MIMEMultipart("mixed" if att_b64 else "alternative")
|
||||
if body:
|
||||
message.attach(MIMEText(body, "plain", "utf-8"))
|
||||
if html_body:
|
||||
message.attach(MIMEText(html_body, "html", "utf-8"))
|
||||
if att_b64:
|
||||
name = att_name or "attachment.bin"
|
||||
ctype = mimetypes.guess_type(name)[0] or "application/octet-stream"
|
||||
main_type, sub_type = ctype.split("/", 1)
|
||||
part = MIMEBase(main_type, sub_type)
|
||||
part.set_payload(base64.b64decode(att_b64))
|
||||
encoders.encode_base64(part)
|
||||
part.add_header("Content-Disposition", "attachment", filename=name)
|
||||
message.attach(part)
|
||||
else:
|
||||
message = MIMEText(body, "plain", "utf-8")
|
||||
|
||||
message["From"] = mailbox
|
||||
message["To"] = str(inputs.get("to") or "")
|
||||
if inputs.get("cc"):
|
||||
message["Cc"] = str(inputs["cc"])
|
||||
if inputs.get("bcc"):
|
||||
message["Bcc"] = str(inputs["bcc"])
|
||||
message["Subject"] = str(inputs.get("subject") or "")
|
||||
if inputs.get("reply_to"):
|
||||
message["Reply-To"] = str(inputs["reply_to"])
|
||||
if inputs.get("in_reply_to"):
|
||||
message["In-Reply-To"] = str(inputs["in_reply_to"])
|
||||
if inputs.get("references"):
|
||||
message["References"] = str(inputs["references"])
|
||||
return message
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
if not str(inputs.get("to") or "").strip():
|
||||
raise Exception("to is required")
|
||||
if not str(inputs.get("subject") or "").strip():
|
||||
raise Exception("subject is required")
|
||||
|
||||
message = _build_message(inputs, mailbox)
|
||||
raw = base64.urlsafe_b64encode(message.as_bytes()).decode("ascii")
|
||||
res = request("POST", base + "/messages/send", SCOPES, body={"raw": raw}, subject=mailbox)
|
||||
print(json.dumps(res))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,83 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/admin.directory.user"]
|
||||
ADMIN_BASE = "https://admin.googleapis.com/admin/directory/v1"
|
||||
|
||||
|
||||
def main():
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
user_key = str(inputs.get("user_key") or "").strip()
|
||||
if not user_key:
|
||||
raise Exception("user_key is required")
|
||||
password = str(inputs.get("password") or "")
|
||||
if not password:
|
||||
raise Exception("password is required")
|
||||
|
||||
result = request("PUT", ADMIN_BASE + "/users/" + urllib.parse.quote(user_key, safe=""), SCOPES,
|
||||
body={"password": password})
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,78 @@
|
||||
import json, os, sys, time, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
import jwt
|
||||
|
||||
TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _token(scopes, subject=None):
|
||||
cfg = _cfg()
|
||||
raw = cfg.get("service_account_json", "")
|
||||
sa = json.loads(raw) if isinstance(raw, str) else raw
|
||||
if not sa.get("client_email") or not sa.get("private_key"):
|
||||
raise Exception("service_account_json must contain client_email and private_key")
|
||||
now = int(time.time())
|
||||
aud = sa.get("token_uri") or TOKEN_URL
|
||||
payload = {
|
||||
"iss": sa["client_email"],
|
||||
"scope": " ".join(scopes),
|
||||
"aud": aud,
|
||||
"iat": now,
|
||||
"exp": now + 3600,
|
||||
}
|
||||
sub = subject or cfg.get("user_id") or ""
|
||||
if sub:
|
||||
payload["sub"] = sub
|
||||
assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
|
||||
data = urllib.parse.urlencode({
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"assertion": assertion,
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(aud, data=data,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Token request failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, url, scopes, params=None, body=None, subject=None):
|
||||
q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
|
||||
if q:
|
||||
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
||||
|
||||
|
||||
def main():
|
||||
mailbox = str(_cfg().get("user_id") or "me")
|
||||
base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")
|
||||
|
||||
res = request("GET", base + "/profile", SCOPES, subject=mailbox)
|
||||
if not res.get("emailAddress"):
|
||||
raise Exception("Profile response missing emailAddress: " + json.dumps(res))
|
||||
print(json.dumps({"ok": True, "email": res["emailAddress"]}))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user