smartpush 1.3.8__py3-none-any.whl → 1.3.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -100,31 +100,51 @@ def mock_pulsar(mock_domain, pulsar, limit=1):
100
100
 
101
101
  def check_flow(mock_domain, host_domain, cookies, **kwargs):
102
102
  """
103
+ 完整触发流程
103
104
  params
104
105
  mock_domain:必填,触发接口域名
105
106
  host_domain:必填,spflow接口域名
106
107
  cookies:必填,sp登录态
107
108
  flow_id:必填
108
109
  pulsar:必填,模拟的触发数据
110
+ old_flow_counts: is_split_steps不为all时必填,参数内容为步骤1返回的old_flow_counts
111
+
109
112
  limit:非必填,默认为1 - mock_pulsar函数用于控制模拟触发的次数
110
- num:非必填,默认为1 - compare_lists函数用于断言方法做差值计算
113
+ sleep_time: 非必填, 默认60s, 等待时间,用于触发后等待各节点计数后获取新数据
111
114
  update_flow_params: 非必填,dict格式,需更新flow时传参,参数结构为sp的saveFlow接口内容
115
+ num:非必填,默认为1 - compare_lists函数用于断言方法做差值计算
116
+ all_key: 非必填,bool,默认false,输入true时,检查指标节点常用5个字段
117
+ check_key: 非必填, 默认只有completedCount, list格式,传入需检查节点的指标key,如:completedCount、skippedCount、openRate等
112
118
  """
113
- # 触发前提取flow数据,后续做对比
114
- old_flow_counts, old_versions = get_current_flow(host_domain=host_domain, cookies=cookies,
115
- flow_id=kwargs["flow_id"])
116
- # 更新flow
117
- if kwargs.get("update_flow_params", False):
118
- update_flow(host_domain=host_domain, cookies=cookies, update_flow_params=kwargs.get("update_flow_params"),
119
- version=old_versions)
120
- # 启动flow
121
- start_flow(host_domain=host_domain, cookies=cookies, flow_id=kwargs["flow_id"], version=old_versions)
122
- # 触发flow
123
- mock_pulsar(mock_domain=mock_domain, pulsar=kwargs["pulsar"], limit=kwargs.get("limit", 1))
124
- # 触发后提取flow数据,做断言
125
- time.sleep(30)
126
- new_flow_counts, new_versions = get_current_flow(host_domain=host_domain, cookies=cookies,
127
- flow_id=kwargs["flow_id"])
128
- # 断言
129
- result = ListDictUtils.compare_lists(temp1=old_flow_counts, temp2=new_flow_counts, num=kwargs.get("num", 1))
130
- return [True, "断言成功"] if len(result) == 0 else [False, result]
119
+ # todo: 还差邮件校验部分,后续补充
120
+ is_split_steps = kwargs.get("split_steps", "all")
121
+ # 步骤1
122
+ if is_split_steps == "one" or is_split_steps == "all":
123
+ # 触发前提取flow数据,后续做对比
124
+ old_flow_counts, old_versions = get_current_flow(host_domain=host_domain, cookies=cookies,
125
+ flow_id=kwargs["flow_id"])
126
+ kwargs["old_flow_counts"] = old_flow_counts
127
+ # 更新flow
128
+ if kwargs.get("update_flow_params", False):
129
+ update_flow(host_domain=host_domain, cookies=cookies, update_flow_params=kwargs.get("update_flow_params"),
130
+ version=old_versions)
131
+ # 启动flow
132
+ start_flow(host_domain=host_domain, cookies=cookies, flow_id=kwargs["flow_id"], version=old_versions)
133
+ # 触发flow
134
+ mock_pulsar(mock_domain=mock_domain, pulsar=kwargs["pulsar"], limit=kwargs.get("limit", 1))
135
+ if is_split_steps == "one":
136
+ return old_flow_counts, old_versions
137
+
138
+ # 步骤2
139
+ if is_split_steps == "two" or is_split_steps == "all":
140
+ if is_split_steps == "all":
141
+ time.sleep(kwargs.get("sleep_time", 60))
142
+ # 触发后提取flow数据,做断言
143
+ new_flow_counts, new_versions = get_current_flow(host_domain=host_domain, cookies=cookies,
144
+ flow_id=kwargs["flow_id"])
145
+ # 断言
146
+ result = ListDictUtils.compare_lists(temp1=kwargs.get("old_flow_counts"),
147
+ temp2=new_flow_counts, num=kwargs.get("num", 1),
148
+ check_key=kwargs.get("check_key", ["completedCount"]),
149
+ all_key=kwargs.get("all_key", False))
150
+ return [True, "断言成功"] if len(result) == 0 else [False, result]
smartpush/test.py CHANGED
@@ -9,8 +9,10 @@ from smartpush.export.basic.ReadExcel import read_excel_and_write_to_dict
9
9
  from smartpush.export.basic.GetOssUrl import get_oss_address_with_retry
10
10
  from smartpush.utils.DataTypeUtils import DataTypeUtils
11
11
  from smartpush.flow import MockFlow
12
+ from smartpush.utils import EmailUtlis
12
13
 
13
14
  if __name__ == '__main__':
15
+ # 导出流程
14
16
  oss1 = "https://cdn.smartpushedm.com/material_ec2/2025-02-26/31c1a577af244c65ab9f9a984c64f3d9/ab%E5%BC%B9%E7%AA%97%E6%B5%8B%E8%AF%952.10%E5%88%9B%E5%BB%BA-%E6%9C%89%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%E9%94%80%E5%94%AE%E9%A2%9D%E6%98%8E%E7%BB%86%E6%95%B0%E6%8D%AE.xlsx"
15
17
  oss2 = "https://cdn.smartpushedm.com/material_ec2/2025-02-26/31c1a577af244c65ab9f9a984c64f3d9/ab%E5%BC%B9%E7%AA%97%E6%B5%8B%E8%AF%952.10%E5%88%9B%E5%BB%BA-%E6%9C%89%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%E9%94%80%E5%94%AE%E9%A2%9D%E6%98%8E%E7%BB%86%E6%95%B0%E6%8D%AE.xlsx"
16
18
  # # print(check_excel_all(oss1, oss1))
@@ -37,10 +39,10 @@ if __name__ == '__main__':
37
39
  # errors = ExcelExportChecker.check_field_format(actual_oss=oss1, fileds={0: {5: "time"}}, skiprows=1)
38
40
  # ExcelExportChecker.check_excel_name(actual_oss=oss1, expected_oss=url)
39
41
 
42
+ # flow触发流程
40
43
  _url = "http://sp-go-flow-test.inshopline.com"
41
44
  host_domain = "https://test.smartpushedm.com/api-em-ec2"
42
- cookies = "_ga=GA1.1.88071637.1717860341; _ga_NE61JB8ZM6=GS1.1.1718954972.32.1.1718954972.0.0.0; _ga_Z8N3C69PPP=GS1.1.1723104149.2.0.1723104149.0.0.0; _ga_D2KXR23WN3=GS1.1.1735096783.3.1.1735096812.0.0.0; osudb_lang=; a_lang=zh-hans-cn; osudb_uid=4213785218; osudb_oar=#01#SID0000126BGKWZjG4n42Q2CwFh4CS1WDoQZHTsZddVzHm5AvTJYrgIBGBQYLWO+XpYs47JMugUA6ZpHRvCdRTXw0OLXxpvdGnuT8GZ5qgcuWxiOIUHwdOCKPO9aEBTTB6NWeShMEFpZlU9lLxzcYL6HLlPBHe; osudb_appid=SMARTPUSH; osudb_subappid=1; ecom_http_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDc1MzQzMjYsImp0aSI6IjVlMTMyOWU3LTAwMzItNDIyNS1hY2NmLWFlNDY4ZTUxZDgxMiIsInVzZXJJbmZvIjp7ImlkIjowLCJ1c2VySWQiOiI0MjEzNzg1MjE4IiwidXNlcm5hbWUiOiIiLCJlbWFpbCI6InRlc3RzbWFydDA5NUBnbWFpbC5jb20iLCJ1c2VyUm9sZSI6Im93bmVyIiwicGxhdGZvcm1UeXBlIjo3LCJzdWJQbGF0Zm9ybSI6MSwicGhvbmUiOiIiLCJsYW5ndWFnZSI6InpoLWhhbnMtY24iLCJhdXRoVHlwZSI6IiIsImF0dHJpYnV0ZXMiOnsiY291bnRyeUNvZGUiOiJDTiIsImN1cnJlbmN5IjoiVVNEIiwiY3VycmVuY3lTeW1ib2wiOiJVUyQiLCJkb21haW4iOiJmZWxpeC10Yy5teXNob3BsaW5lc3RnLmNvbSIsImxhbmd1YWdlIjoiZW4iLCJtZXJjaGFudEVtYWlsIjoidGVzdHNtYXJ0MDk1QGdtYWlsLmNvbSIsIm1lcmNoYW50TmFtZSI6ImZlbGl4LXRjIiwicGhvbmUiOiIiLCJzY29wZUNoYW5nZWQiOnRydWUsInN0YWZmTGFuZ3VhZ2UiOm51bGwsInN0YXR1cyI6MiwidGltZXpvbmUiOiJBc2lhL0hvbmdfS29uZyJ9LCJzdG9yZUlkIjoiMTY2NTY2MTA4NDM2MyIsImhhbmRsZSI6ImZlbGl4LXRjIiwiZW52IjoiQ04iLCJzdGUiOiIiLCJ2ZXJpZnkiOiIifSwibG9naW5UaW1lIjoxNzQ0OTQyMzI2NTM0LCJzY29wZSI6WyJlbWFpbC1tYXJrZXQiLCJjb29raWUiLCJzbC1lY29tLWVtYWlsLW1hcmtldC1uZXctdGVzdCIsImVtYWlsLW1hcmtldC1uZXctZGV2LWZzIiwiYXBpLXVjLWVjMiIsImFwaS1zdS1lYzIiLCJhcGktZW0tZWMyIiwiZmxvdy1wbHVnaW4iLCJhcGktc3AtbWFya2V0LWVjMiJdLCJjbGllbnRfaWQiOiJlbWFpbC1tYXJrZXQifQ.rBrzepA8U8ghqLcNGGF4N6s6PXA6v6tJaKVOe5jQdaw; JSESSIONID=0228F95DD250A037C91E0E2927EE2FC7"
43
-
45
+ cookies = "a_lang=zh-hans-cn; ecom_http_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDc3OTMyMzgsImp0aSI6ImU1YWY4ZGE5LTlhMTQtNGY0Yi04NmM5LTNlNzAwZTFjY2RiYyIsInVzZXJJbmZvIjp7ImlkIjowLCJ1c2VySWQiOiI0MjEzNzg1MjQ3IiwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImZlbGl4LnNoYW9Ac2hvcGxpbmVhcHAuY29tIiwidXNlclJvbGUiOiJvd25lciIsInBsYXRmb3JtVHlwZSI6Nywic3ViUGxhdGZvcm0iOjEsInBob25lIjoiIiwibGFuZ3VhZ2UiOiJ6aC1oYW5zLWNuIiwiYXV0aFR5cGUiOiIiLCJhdHRyaWJ1dGVzIjp7ImNvdW50cnlDb2RlIjoiQ04iLCJjdXJyZW5jeSI6IkpQWSIsImN1cnJlbmN5U3ltYm9sIjoiSlDCpSIsImRvbWFpbiI6InNtYXJ0cHVzaDQubXlzaG9wbGluZXN0Zy5jb20iLCJsYW5ndWFnZSI6ImVuIiwibWVyY2hhbnRFbWFpbCI6ImZlbGl4LnNoYW9Ac2hvcGxpbmUuY29tIiwibWVyY2hhbnROYW1lIjoiU21hcnRQdXNoNF9lYzJf6Ieq5Yqo5YyW5bqX6ZO6IiwicGhvbmUiOiIiLCJzY29wZUNoYW5nZWQiOnRydWUsInN0YWZmTGFuZ3VhZ2UiOiJ6aC1oYW5zLWNuIiwic3RhdHVzIjowLCJ0aW1lem9uZSI6IkFzaWEvTWFjYW8ifSwic3RvcmVJZCI6IjE2NDQzOTU5MjA0NDQiLCJoYW5kbGUiOiJzbWFydHB1c2g0IiwiZW52IjoiQ04iLCJzdGUiOiIiLCJ2ZXJpZnkiOiIifSwibG9naW5UaW1lIjoxNzQ1MjAxMjM4NjA0LCJzY29wZSI6WyJlbWFpbC1tYXJrZXQiLCJjb29raWUiLCJzbC1lY29tLWVtYWlsLW1hcmtldC1uZXctdGVzdCIsImVtYWlsLW1hcmtldC1uZXctZGV2LWZzIiwiYXBpLXVjLWVjMiIsImFwaS1zdS1lYzIiLCJhcGktZW0tZWMyIiwiZmxvdy1wbHVnaW4iLCJhcGktc3AtbWFya2V0LWVjMiJdLCJjbGllbnRfaWQiOiJlbWFpbC1tYXJrZXQifQ.NgpEGF_8BJgTOh1LNz_Is43JBd7uZhyTGQtQq8SOdsc; osudb_appid=SMARTPUSH; osudb_lang=; osudb_oar=#01#SID0000126BLmmyWXDjlSc59ljxo2axjoWExJ1oHkAkRqVtfzm4UUtWvBGvzF6FeTbrvTHmfJLrtJz+Dpthge0dIaJpI2ukTujazNNJbLjZFu9kkuOrtRAXNL02fNqpUb6MwTCNxOnm03YIlfb7X2482UvDm36; osudb_subappid=1; osudb_uid=4213785247; JSESSIONID=8DEE7103CFA4505DF68ECE629B00A0F3"
44
46
 
45
47
  params = {
46
48
  "abandonedOrderId": "c2c4a695a36373f56899b370d0f1b6f2",
@@ -91,8 +93,100 @@ if __name__ == '__main__':
91
93
  "uid": "4603296300",
92
94
  "userId": "1911625831177650177"
93
95
  }
94
- mock_pulsar = MockFlow.check_flow(mock_domain=_url, host_domain=host_domain, cookies=cookies,
95
- flow_id="FLOW6749144046546626518", pulsar=params)
96
- # node_counts, versions = MockFlow.get_current_flow(host_domain=host_domain, cookies=cookies,
96
+ update_flow_params = {"id": "FLOW6941975456855532553", "version": "10", "triggerId": "c1001",
97
+ "templateId": "TEMP6911595896571704333", "showData": False, "flowChange": True, "nodes": [
98
+ {"type": "trigger", "data": {
99
+ "trigger": {"trigger": "c1001", "group": "", "suggestionGroupId": "", "triggerStock": False,
100
+ "completedCount": 4, "skippedCount": 0}, "completedCount": 4, "skippedCount": 0},
101
+ "id": "92d115e7-8a86-439a-8cfb-1aa3ef075edf"}, {"type": "delay", "data": {
102
+ "delay": {"type": "relative", "relativeTime": 0, "relativeUnit": "HOURS", "designatedTime": ""},
103
+ "completedCount": 4}, "id": "e0fc258b-fcfc-421c-b215-8e41638072ca"}, {"type": "sendLetter", "data": {
104
+ "sendLetter": {"id": 367462, "activityTemplateId": 367462, "activityName": "flowActivity_EwEi3d",
105
+ "activityImage": "http://cdn.smartpushedm.com/frontend/smart-push/staging/1644395920444/1744102089665/1744102093754_f99e3703.jpeg",
106
+ "emailName": "A Message from Your Cart", "merchantId": "1644395920444",
107
+ "merchantName": "SmartPush4_ec2_自动化店铺",
108
+ "brandName": "SmartPush4_ec2_自动化店铺 AutoTestName", "currency": "JP¥",
109
+ "activityType": "NORMAL", "activityStatus": "ACTIVE", "createTime": 1745201732286,
110
+ "updateTime": 1745201825819, "createDate": "2025-04-21 10:15:32",
111
+ "updateDate": "2025-04-21 10:17:05", "pickContactPacks": [], "excludeContactPacks": [],
112
+ "customerGroupIds": [], "excludeCustomerGroupIds": [], "pickContactInfos": [],
113
+ "excludeContactInfos": [], "customerGroupInfos": [], "excludeCustomerGroupInfos": [],
114
+ "sender": "SmartPush4_ec2_自动化店铺", "senderDomain": "DEFAULT_DOMAIN", "domainType": 3,
115
+ "receiveAddress": "", "originTemplate": 33,
116
+ "currentJsonSchema": "{\"id\":\"a4a9fba2a\",\"type\":\"Stage\",\"props\":{\"backgroundColor\":\"#EAEDF1\",\"width\":\"600px\",\"fullWidth\":\"normal-width\"},\"children\":[{\"id\":\"84ba788da\",\"type\":\"Header\",\"props\":{\"backgroundColor\":\"#ffffff\",\"borderLeft\":\"1px none #ffffff\",\"borderRight\":\"1px none #ffffff\",\"borderTop\":\"1px none #ffffff\",\"borderBottom\":\"1px none #ffffff\",\"paddingTop\":\"0px\",\"paddingBottom\":\"0px\",\"paddingLeft\":\"0px\",\"paddingRight\":\"0px\",\"cols\":[12]},\"children\":[{\"id\":\"98d909a48\",\"type\":\"Column\",\"props\":{},\"children\":[]}]},{\"id\":\"84ba7bbda\",\"type\":\"Section\",\"props\":{\"backgroundColor\":\"#ffffff\",\"borderLeft\":\"1px none #ffffff\",\"borderRight\":\"1px none #ffffff\",\"borderTop\":\"1px none #ffffff\",\"borderBottom\":\"1px none #ffffff\",\"paddingTop\":\"0px\",\"paddingBottom\":\"0px\",\"paddingLeft\":\"0px\",\"paddingRight\":\"0px\",\"cols\":[12]},\"children\":[{\"id\":\"8cab9aa48\",\"type\":\"Column\",\"props\":{},\"children\":[]}]},{\"id\":\"b8bbabad9\",\"type\":\"Footer\",\"props\":{\"backgroundColor\":\"#ffffff\",\"borderLeft\":\"1px none #ffffff\",\"borderRight\":\"1px none #ffffff\",\"borderTop\":\"1px none #ffffff\",\"borderBottom\":\"1px none #ffffff\",\"paddingTop\":\"0px\",\"paddingBottom\":\"0px\",\"paddingLeft\":\"0px\",\"paddingRight\":\"0px\",\"cols\":[12]},\"children\":[{\"id\":\"b3bcabad7\",\"type\":\"Column\",\"props\":{},\"children\":[{\"id\":\"b39b6a94a\",\"type\":\"Subscribe\",\"props\":{\"content\":\"<p style=\\\"text-align:center;\\\"><span style=\\\"font-size:12px\\\"><span style=\\\"font-family:Arial, Helvetica, sans-serif\\\">在此處輸入聯繫地址,可以讓你的顧客更加信任這封郵件</span></span></p>\"},\"children\":[]}]}]}],\"extend\":{\"version\":\"1.0.0\",\"updateTime\":\"2025-03-18T09:57:40.953Z\"}}",
117
+ "currentHtml": "<!doctype html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n <head>\n <title></title>\n <!--[if !mso]><!-->\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <!--<![endif]-->\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <style type=\"text/css\">\n #outlook a { padding:0; }\n body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }\n table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }\n img { border:0;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }\n pre{margin: 0;}p{ display: block;margin:0;}\n </style>\n <!--[if mso]>\n <noscript>\n <xml>\n <o:OfficeDocumentSettings>\n <o:AllowPNG/>\n <o:PixelsPerInch>96</o:PixelsPerInch>\n </o:OfficeDocumentSettings>\n </xml>\n </noscript>\n <![endif]-->\n <!--[if lte mso 11]>\n <style type=\"text/css\">\n .mj-outlook-group-fix { width:100% !important; }\n </style>\n <![endif]-->\n \n <!--[if !mso]><!-->\n <link href=\"https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700\" rel=\"stylesheet\" type=\"text/css\">\n <style type=\"text/css\">\n @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);\n </style>\n <!--<![endif]-->\n\n \n \n <style type=\"text/css\">\n @media only screen and (min-width:480px) {\n .mj-column-per-100 { width:100% !important; max-width: 100%; }\n }\n </style>\n <style media=\"screen and (min-width:480px)\">\n .moz-text-html .mj-column-per-100 { width:100% !important; max-width: 100%; }\n </style>\n \n \n <style type=\"text/css\">\n \n \n </style>\n <style type=\"text/css\">\n @import url(https://fonts.googleapis.com/css?family=Arvo:400|Bodoni+Moda:400|DM+Sans:400|Poppins:400|Hammersmith+One:400|Libre+Baskerville:400|Lexend+Giga:400|Ubuntu:400|Montserrat:400|Nunito:400|News+Cycle:400|Roboto:400|Oswald:400);@import url(https://fonts.googleapis.com/css?family=Asar:400|Bruno+Ace:400|Cantata+One:400|League+Gothic:400|Long+Cang:400|Lovers+Quarrel:400|Nanum+Gothic+Coding:400|Nanum+Myeongjo:400|Noto+Sans+Kaithi:400|Noto+Sans+Kannada:400|Noto+Sans+Math:400|Noto+Sans+Syloti+Nagri:400|Noto+Serif+JP:400|Playwrite+AT+Guides:400|Saira+Extra+Condensed:400|Tsukimi+Rounded:400|Waiting+for+the+Sunrise:400);h1,\nh2,\nh3,\nh4,\nh5 {\n font-weight: bold;\n margin-bottom: 0;\n}\np {\n margin-top: 0;\n margin-bottom: 0;\n min-height: 1em;\n}\n\nul {\n margin-bottom: 0;\n}\n\nth {\n font-weight: bold;\n}\n\na {\n text-decoration: none;\n}\n\na::-webkit-scrollbar {\n -webkit-appearance: none;\n}\n\na::-webkit-scrollbar:horizontal {\n max-height: 8px;\n}\n\na::-webkit-scrollbar-thumb {\n border-radius: 8px;\n background-color: rgba(0, 0, 0, 0.5);\n}\n\nul,\nol,\ndl {\n margin-top: 16px !important;\n}\n\npre {\n word-break: break-word;\n padding: 0;\n margin: 0;\n white-space: inherit !important;\n}\n\npre p {\n word-break: break-word;\n padding: 0;\n margin: 0;\n color: #000000;\n}\n\nspan[style*='color'] a {\n color: inherit;\n}\n\n.mj-column-no-meida-100{\n width: 100% !important;\n}\n.mj-column-no-meida-50{\n width: 50% !important;\n}\n.mj-column-no-meida-33-333333333333336{\n width: 33.333333333333336% !important;\n}\n.mj-column-no-meida-25{\n width: 25% !important;\n}\n\n.white-nowrap {\n white-space: nowrap !important;\n}\n\n/* 间距 */\n.sp-m-p-0 {\n margin: 0;\n padding: 0;\n}\n\n.nps-content-span a {\n color: inherit !important;\n}.sp-font-12 {\n font-size: 12px !important;\n}\n\n.sp-font-14 {\n font-size: 14px !important;\n}\n\n.sp-font-16 {\n font-size: 16px !important;\n}\n\n.sp-font-18 {\n font-size: 18px !important;\n}\n\n.sp-font-20 {\n font-size: 20px !important;\n}\n\n.sp-font-22 {\n font-size: 22px !important;\n}\n\n.sp-font-24 {\n font-size: 24px !important;\n}\n\n.sp-font-26 {\n font-size: 26px !important;\n}\n\n.sp-font-28 {\n font-size: 28px !important;\n}\n\n.sp-font-30 {\n font-size: 30px !important;\n}\n\n.sp-font-32 {\n font-size: 32px !important;\n}\n\n.sp-font-34 {\n font-size: 34px !important;\n}\n\n.sp-font-36 {\n font-size: 36px !important;\n}\n\n.sp-font-38 {\n font-size: 38px !important;\n}\n\n.sp-font-40 {\n font-size: 40px !important;\n}\n\n.sp-font-42 {\n font-size: 42px !important;\n}\n\n.sp-font-44 {\n font-size: 44px !important;\n}\n\n.sp-font-46 {\n font-size: 46px !important;\n}\n\n.sp-font-48 {\n font-size: 48px !important;\n}\n\n.sp-font-50 {\n font-size: 50px !important;\n}\n\n.sp-font-52 {\n font-size: 52px !important;\n}\n\n.sp-font-54 {\n font-size: 54px !important;\n}\n\n.sp-font-56 {\n font-size: 56px !important;\n}\n\n.sp-font-58 {\n font-size: 58px !important;\n}\n\n.sp-font-60 {\n font-size: 60px !important;\n}\n\n.sp-image-icon {\n width: 50px;\n}\n\n@media only screen and (max-width:600px) {\n\n .sp-font-12 {\n font-size: 12px !important;\n }\n\n .sp-font-14 {\n font-size: 12px !important;\n }\n\n .sp-font-16 {\n font-size: 12px !important;\n }\n\n .sp-font-18 {\n font-size: 13px !important;\n }\n\n .sp-font-20 {\n font-size: 15px !important;\n }\n\n .sp-font-22 {\n font-size: 16px !important;\n }\n\n .sp-font-24 {\n font-size: 18px !important;\n }\n\n .sp-font-26 {\n font-size: 19px !important;\n }\n\n .sp-font-28 {\n font-size: 21px !important;\n }\n\n .sp-font-30 {\n font-size: 22px !important;\n }\n\n .sp-font-32 {\n font-size: 24px !important;\n }\n\n .sp-font-34 {\n font-size: 25px !important;\n }\n\n .sp-font-36 {\n font-size: 27px !important;\n }\n\n .sp-font-38 {\n font-size: 28px !important;\n }\n\n .sp-font-40 {\n font-size: 30px !important;\n }\n\n .sp-font-42 {\n font-size: 31px !important;\n }\n\n .sp-font-44 {\n font-size: 32px !important;\n }\n\n .sp-font-46 {\n font-size: 33px !important;\n }\n\n .sp-font-48 {\n font-size: 34px !important;\n }\n\n .sp-font-50 {\n font-size: 35px !important;\n }\n\n .sp-font-52 {\n font-size: 36px !important;\n }\n\n .sp-font-54 {\n font-size: 37px !important;\n }\n\n .sp-font-56 {\n font-size: 38px !important;\n }\n\n .sp-font-58 {\n font-size: 39px !important;\n }\n\n .sp-font-60 {\n font-size: 40px !important;\n }\n\n .sp-image-icon {\n width: 28px !important;\n }\n\n}@media only screen and (max-width:480px) {\n\n .sp-img-h-1-TwoVertical-11,\n .sp-img-h-1-TwoHorizontalColumns-11 {\n height: 170px !important;\n }\n\n .sp-img-h-1-TwoVertical-23,\n .sp-img-h-1-TwoHorizontalColumns-23 {\n height: 243px !important;\n }\n\n .sp-img-h-1-TwoVertical-34,\n .sp-img-h-1-TwoHorizontalColumns-34 {\n height: 227px !important;\n }\n\n .sp-img-h-1-TwoVertical-43,\n .sp-img-h-1-TwoHorizontalColumns-43 {\n height: 127px !important;\n }\n\n .sp-img-h-1-ThreeHorizontalColumns-11 {\n height: 109px !important;\n }\n\n .sp-img-h-1-ThreeHorizontalColumns-23 {\n height: 163px !important;\n }\n\n .sp-img-h-1-ThreeHorizontalColumns-34 {\n height: 145px !important;\n }\n\n .sp-img-h-1-ThreeHorizontalColumns-43 {\n height: 81px !important;\n }\n\n .sp-img-h-2-TwoVertical-11 {\n height: 164px !important;\n }\n\n .sp-img-h-2-TwoVertical-23 {\n height: 246px !important;\n }\n\n .sp-img-h-2-TwoVertical-34 {\n height: 218px !important;\n }\n\n .sp-img-h-2-TwoVertical-43 {\n height: 123px !important;\n }\n\n .sp-img-h-2-ThreeHorizontalColumns-11,\n .sp-img-h-2-TwoHorizontalColumns-11 {\n height: 76px !important;\n }\n\n .sp-img-h-2-ThreeHorizontalColumns-23,\n .sp-img-h-2-TwoHorizontalColumns-23 {\n height: 113px !important;\n }\n\n .sp-img-h-2-ThreeHorizontalColumns-34,\n .sp-img-h-2-TwoHorizontalColumns-34 {\n height: 101px !important;\n }\n\n .sp-img-h-2-ThreeHorizontalColumns-43,\n .sp-img-h-2-TwoHorizontalColumns-43 {\n height: 57px !important;\n }\n}@media only screen and (min-width: 320px) and (max-width: 599px) {\n .mj-w-50 {\n width: 50% !important;\n max-width: 50%;\n }\n}\n\n@media only screen and (max-width: 480px) {\n .shop-white-mobile {\n width: 100% !important;\n }\n .mj-td-force-100{\n display: inline-block;\n width: 100% !important;\n }\n\n .mj-ImageText-screen {\n width: 100% !important;\n }\n .mj-ImageText-img {\n margin: 0 auto;\n }\n\n .mj-ImageText-margin {\n margin: 0 !important;\n }\n\n .mj-ImageText-margin-zero {\n margin: 0 5px 0 0 !important;\n }\n\n .mj-ImageText-margin-one {\n margin: 0 0 0 5px !important;\n }\n .mj-column-per-50-force {\n width: 50% !important;\n max-width: 50%;\n }\n}\n\n@media only screen and (min-width: 480px) {\n .mj-imagetext-force-100{\n display: inline-block;\n width: 100% !important;\n }\n .mj-column-per-100 {\n width: 100% !important;\n max-width: 100%;\n }\n\n .mj-column-per-50 {\n width: 50% !important;\n max-width: 50%;\n }\n\n .mj-column-per-33-333333333333336 {\n width: 33.333333333333336% !important;\n max-width: 33.333333333333336%;\n }\n\n .mj-column-per-25 {\n width: 25% !important;\n max-width: 25%;\n }\n\n\n .mg-column-per-46-5 {\n width: 46.5% !important;\n max-width: 46.5%;\n }\n\n .mj-AbandonProduct-cloum-align-center {\n align-items: flex-start !important;\n }\n\n\n .mj-OrderInformation-padding-top-220 {\n padding-top: 20px !important;\n }\n\n .mj-OrderInformation-float-left {\n float: left !important;\n }\n}@media only screen and (max-width: 480px) {\n .mt-1{\n margin-top: 1px;\n }\n .mt-2{\n margin-top: 2px;\n }\n .mt-3{\n margin-top: 3px;\n }\n .mt-4{\n margin-top: 4px;\n }\n .mt-5{\n margin-top: 5px;\n }\n .mt-6{\n margin-top: 7px;\n }\n .mt-8{\n margin-top: 9px;\n }\n .mt-10{\n margin-top: 10px;\n }\n\n}\n </style>\n \n </head>\n <body style=\"word-spacing:normal;background-color:#EAEDF1;\">\n \n \n <div\n style=\"background-color:#EAEDF1;\"\n >\n <table className=\"pv-stage\">\n <tbody>\n <tr>\n <td style=\"display:table-column;\"><div style=\"width:1px; height:1px;\"><img style=\"width:1px; height:1px;\" width=\"1\" src=\"${SP_OPEN_EMAIL_URL}\" />\n </div></td>\n </tr>\n </tbody>\n </table><div mso-hide: all; position: fixed; height: 0; max-height: 0; overflow: hidden; font-size: 0; style=\"display:none;\">${emailSubtitle}</div>\n \n <!--[if mso | IE]><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\" role=\"presentation\" style=\"width:600px;\" width=\"600\" bgcolor=\"#ffffff\" ><tr><td style=\"line-height:0px;font-size:0px;mso-line-height-rule:exactly;\"><![endif]-->\n \n \n <div style=\"background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;\">\n \n <table\n align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#ffffff;background-color:#ffffff;width:100%;\"\n >\n <tbody>\n <tr>\n <td\n style=\"border-bottom:1px none #ffffff;border-left:1px none #ffffff;border-right:1px none #ffffff;border-top:1px none #ffffff;direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0px;padding-left:0px;padding-right:0px;padding-top:0px;text-align:center;\"\n >\n <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:598px;\" ><![endif]-->\n \n <div\n class=\"mj-column-per-100 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;\"\n >\n \n <table\n border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\"\n >\n <tbody>\n \n <tr>\n <td\n align=\"left\" style=\"font-size:0px;padding:0;word-break:break-word;\"\n >\n \n <table\n cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" border=\"0\" style=\"color:#000000;font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;table-layout:fixed;width:100%;border:none;\"\n >\n <table style=\"margin:0;padding:0;width:100%;table-layout:fixed\" class=\"\" sp-id=\"subscribe-area-b39b6a94a\"><tbody><tr style=\"width:100%\"><td style=\"margin:0;padding:0;width:100%;text-align:center;padding-top:20px;padding-left:20px;padding-right:20px;padding-bottom:20px;background-color:transparent;font-family:arial,helvetica,sans-serif,Arial, Helvetica, sans-serif\" class=\"Subscribe\"><table cellPadding=\"0\" cellSpacing=\"0\" style=\"width:100%\"><tbody><tr><td align=\"center\" class=\"sp-font-16\" valign=\"middle\" style=\"padding-left:10px;padding-right:10px;padding-top:10px;text-align:left;font-size:16px\"><div><p style=\"text-align:center;\"><span style=\"font-size:12px\"><span style=\"font-family:Arial, Helvetica, sans-serif\">在此處輸入聯繫地址,可以讓你的顧客更加信任這封郵件</span></span></p></div></td></tr><tr><td align=\"center\" valign=\"middle\" height=\"20\" style=\"font-size:12px;font-family:arial,helvetica,sans-serif,Arial, Helvetica, sans-serif;padding-left:10px;padding-right:10px;padding-bottom:20px\"></td></tr><div><tr style=\"${hide_logo}\" sp-id=\"subscribe-dom-b39b6a94a\">\n <td class='sp-font-16' style=\"padding-left:20px;padding-right:20px;padding-top:20px;text-align:center;font-size:16px\" >\n <div style=\"border-top: 1px solid #EEF1F6\">\n <img src=\"https://cdn.smartpushedm.com/frontend/smart-push/product/image/1731577171577_83853d55.png\" style=\"padding: 10px;vertical-align: middle;width: 158px;\"alt=\"\" />\n </div>\n <p style=\"color:#343434;font-size:12px\">Providing content services for [[shopName]]</p>\n </td>\n </tr></div></tbody></table></td></tr></tbody></table>\n </table>\n \n </td>\n </tr>\n \n </tbody>\n </table>\n \n </div>\n \n <!--[if mso | IE]></td></tr></table><![endif]-->\n </td>\n </tr>\n </tbody>\n </table>\n \n </div>\n \n \n <!--[if mso | IE]></td></tr></table><![endif]-->\n \n \n </div>\n \n </body>\n</html>\n ",
118
+ "previewJsonSchema": "{\"id\":\"a4a9fba2a\",\"type\":\"Stage\",\"props\":{\"backgroundColor\":\"#EAEDF1\",\"width\":\"600px\",\"fullWidth\":\"normal-width\"},\"children\":[{\"id\":\"84ba788da\",\"type\":\"Header\",\"props\":{\"backgroundColor\":\"#ffffff\",\"borderLeft\":\"1px none #ffffff\",\"borderRight\":\"1px none #ffffff\",\"borderTop\":\"1px none #ffffff\",\"borderBottom\":\"1px none #ffffff\",\"paddingTop\":\"0px\",\"paddingBottom\":\"0px\",\"paddingLeft\":\"0px\",\"paddingRight\":\"0px\",\"cols\":[12]},\"children\":[{\"id\":\"98d909a48\",\"type\":\"Column\",\"props\":{},\"children\":[]}]},{\"id\":\"84ba7bbda\",\"type\":\"Section\",\"props\":{\"backgroundColor\":\"#ffffff\",\"borderLeft\":\"1px none #ffffff\",\"borderRight\":\"1px none #ffffff\",\"borderTop\":\"1px none #ffffff\",\"borderBottom\":\"1px none #ffffff\",\"paddingTop\":\"0px\",\"paddingBottom\":\"0px\",\"paddingLeft\":\"0px\",\"paddingRight\":\"0px\",\"cols\":[12]},\"children\":[{\"id\":\"8cab9aa48\",\"type\":\"Column\",\"props\":{},\"children\":[]}]},{\"id\":\"b8bbabad9\",\"type\":\"Footer\",\"props\":{\"backgroundColor\":\"#ffffff\",\"borderLeft\":\"1px none #ffffff\",\"borderRight\":\"1px none #ffffff\",\"borderTop\":\"1px none #ffffff\",\"borderBottom\":\"1px none #ffffff\",\"paddingTop\":\"0px\",\"paddingBottom\":\"0px\",\"paddingLeft\":\"0px\",\"paddingRight\":\"0px\",\"cols\":[12]},\"children\":[{\"id\":\"b3bcabad7\",\"type\":\"Column\",\"props\":{},\"children\":[{\"id\":\"b39b6a94a\",\"type\":\"Subscribe\",\"props\":{\"content\":\"<p style=\\\"text-align:center;\\\"><span style=\\\"font-size:12px\\\"><span style=\\\"font-family:Arial, Helvetica, sans-serif\\\">在此處輸入聯繫地址,可以讓你的顧客更加信任這封郵件</span></span></p>\"},\"children\":[]}]}]}],\"extend\":{\"version\":\"1.0.0\",\"updateTime\":\"2025-03-18T09:57:40.953Z\"}}",
119
+ "generatedHtml": False,
120
+ "templateUrl": "https://cdn2.smartpushedm.com/material/2021-11-29/d4f96fc873e942a397be708c932bbbe4-自定义排版.png",
121
+ "sendStrategy": "NOW", "totalReceiver": 0, "utmConfigEnable": False,
122
+ "subtitle": "Items in your cart are selling out fast!", "language": "en",
123
+ "languageName": "英语", "timezone": "Asia/Shanghai", "timezoneGmt": "GMT+08:00",
124
+ "type": "FLOW", "relId": "FLOW6941975456855532553",
125
+ "parentId": "TEMP6911595896571704333", "nodeId": "2503b475-ce3e-4906-ab04-0ebc387f0d7e",
126
+ "version": "10", "nodeOrder": 0, "sendType": "EMAIL", "productInfos": [], "blocks": [
127
+ {"domId": "subscribe-dom-b39b6a94a", "blockId": "", "areaId": "", "type": "SP_LOGO",
128
+ "column": 1, "fillStyle": 0, "ratio": ""}], "discountCodes": [], "reviews": [], "awards": [],
129
+ "selectProducts": [], "createSource": "BUILD_ACTIVITY", "contentChange": True,
130
+ "activityChange": False, "imageVersion": "1744102089665", "subActivityList": [],
131
+ "warmupPack": 0, "boosterEnabled": False, "smartSending": False, "boosterCreated": False,
132
+ "gmailPromotion": False, "sendTimeType": "FIXED", "sendTimezone": "B_TIMEZONE",
133
+ "sendTimeDelay": False, "sendOption": 1, "hasUserBlock": False, "hasAutoBlock": False,
134
+ "smsSendDelay": True, "payFunctionList": [], "minSendTime": "2025-04-21 10:15:32",
135
+ "completedCount": 4, "skippedCount": 0, "openRate": 1, "clickRate": 0, "orderIncome": 0,
136
+ "openDistinctUserRate": 1, "clickDistinctUserRate": 0}, "completedCount": 4,
137
+ "skippedCount": 0, "openRate": 1, "clickRate": 0, "orderIncome": 0, "openDistinctUserRate": 1,
138
+ "clickDistinctUserRate": 0}, "id": "2503b475-ce3e-4906-ab04-0ebc387f0d7e"}],
139
+ "showDataStartTime": 1745164800000, "showDataEndTime": 1745251199000}
140
+ # mock_pulsar = MockFlow.check_flow(mock_domain=_url, host_domain=host_domain, cookies=cookies,
141
+ # flow_id="FLOW6941975456855532553", pulsar=params,
142
+ # update_flow_params=update_flow_params)
143
+ # print(mock_pulsar)
144
+
145
+ # mock_pulsar_step1, _ = MockFlow.check_flow(mock_domain=_url, host_domain=host_domain, cookies=cookies,
146
+ # flow_id="FLOW6941975456855532553", pulsar=params,
147
+ # update_flow_params=update_flow_params, split_steps="one")
148
+ # mock_pulsar_step2 = MockFlow.check_flow(mock_domain=_url, host_domain=host_domain, cookies=cookies,
149
+ # flow_id="FLOW6941975456855532553", old_flow_counts=mock_pulsar_step1,
150
+ # print(mock_pulsar_step1)
151
+ # print(mock_pulsar_step2)
152
+
153
+ # split_steps="two")
154
+ # # node_counts, versions = MockFlow.get_current_flow(host_domain=host_domain, cookies=cookies,
97
155
  # flow_id="FLOW6749144046546626518")
98
- print(mock_pulsar)
156
+
157
+ # 调试
158
+ # # 示例字典
159
+ # my_dict = {'name': 'Alice', 'age': 25, 'city': 'New York'}
160
+ # # 提取所有键到列表
161
+ # key_list = list(my_dict)
162
+ # print(key_list)
163
+ a = ["1", "3", "5"]
164
+ # b = ["3", "1", "5"]
165
+ # if a.sort() == b.sort():
166
+ # print(1)
167
+ # else:
168
+ # print(2)
169
+
170
+ # 断言邮件
171
+ loginEmail, password = 'lulu9600000@gmail.com', 'evvurakhttndwspx'
172
+ email_property = [{'1AutoTest-固定B-营销-生产2.0-2025-04-24 10:19:47.341333-🔥🔥': {'activityId': 408764,
173
+ 'utmConfigInfo': {'utmSource': '1',
174
+ 'utmMedium': '2',
175
+ 'utmCampaign': '3'},
176
+ 'receiveAddress': 'autotest-smartpushpro5@smartpush.com',
177
+ 'sender': 'SmartPush_Pro5_ec2自动化店铺 AutoTestName',
178
+ 'subtitle': 'AutoTest-2025-04-24 10:19:47.341333-subtitle-[[contact.name]]-🔥🔥'}},
179
+ {'AutoTest-固定B-营销-生产2.0-2025-04-24 10:19:59.023150-🔥🔥': {'activityId': 408765,
180
+ 'utmConfigInfo': {'utmSource': '1',
181
+ 'utmMedium': '2',
182
+ 'utmCampaign': '3'},
183
+ 'receiveAddress': '1autotest-smartpushpro5@smartpush.com',
184
+ 'sender': 'SmartPush_Pro5_ec2自动化店铺 AutoTestName',
185
+ 'subtitle': 'AutoTest-2025-04-24 10:19:59.023150-subtitle-[[contact.name]]-🔥🔥'}},
186
+ {'测试邮件-AutoTest_营销_生产2.0_2025_04_24 10:20:28.529290_🔥🔥-😈': {'activityId': None,
187
+ 'utmConfigInfo': None,
188
+ 'receiveAddress': 'autotest-smartpushpro5@smartpush.com',
189
+ 'sender': '1SmartPush_Pro5_ec2自动化店铺 AutoTestName',
190
+ 'subtitle': '营销测试邮件-2025-04-24 10:20:29.560357-😈'}}]
191
+ result = EmailUtlis.check_email_content(emailProperty=email_property, loginEmail=loginEmail, password=password)
192
+ print(result)
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env python
2
+ # _*_ coding:utf-8 _*_
3
+ import random
4
+ import re
5
+ import ssl
6
+ from datetime import timedelta, datetime
7
+ from urllib.parse import parse_qs
8
+ from retry import retry
9
+ import imapclient
10
+ import pyzmail
11
+ import requests
12
+ from bs4 import BeautifulSoup
13
+ from urllib import parse
14
+
15
+ junkEmail, allEmail = "[Gmail]/垃圾邮件", "[Gmail]/所有邮件"
16
+
17
+
18
+ def forParseEmailContent(UIDs, isReturnHtml=False):
19
+ """
20
+ 解析邮件内容
21
+ @param UIDs:
22
+ @return:
23
+ """
24
+ global _subject, _sender, _from_email, _recipient, _receiveDate, _replay
25
+ messageInfo = {}
26
+ subjectList = []
27
+ if len(UIDs) > 0:
28
+ rawMessages = imapObj.fetch(UIDs, ['BODY[]'])
29
+
30
+ for uid in UIDs:
31
+ message = pyzmail.PyzMessage.factory(rawMessages[uid][b'BODY[]'])
32
+ # 解析邮件内容
33
+ _subject = message.get_subject()
34
+ subjectList.append(_subject)
35
+ _sender = message.get_addresses('from')[0][0]
36
+ _from_email = message.get_addresses('from')[0][1]
37
+ _recipient = message.get_addresses('to')[0][0]
38
+ if message.get_addresses('reply-to'):
39
+ _replay = message.get_addresses('reply-to')[0][0]
40
+ _receiveDate = message.get('date')
41
+ tempData = {"sender": _sender, "from_email": _from_email, "recipient": _recipient,
42
+ "receiveAddress": _replay if _replay else None, "receiveDate": _receiveDate}
43
+ if len(UIDs) == 1:
44
+ messageInfo.update({_subject: tempData})
45
+ else:
46
+ if messageInfo.get(_subject) is None:
47
+ messageInfo.update({_subject: [tempData]})
48
+ else:
49
+ tempList = messageInfo.get(_subject)
50
+ tempList.append(tempData)
51
+ messageInfo.update({_subject: tempList})
52
+ # html内容解析
53
+ if isReturnHtml:
54
+ htmlBody = message.html_part.get_payload().decode(message.html_part.charset)
55
+ # print('邮件html:', htmlBody)
56
+
57
+ if not isReturnHtml:
58
+ # print("------------------")
59
+ # print("邮件主旨:" + _subject)
60
+ # print("发件人:" + _sender)
61
+ # print("发件邮箱:" + _from_email)
62
+ # print("收件人邮箱:" + _recipient)
63
+ # print("回复邮箱:" + _replay)
64
+ # print('接收邮件日期:' + _receiveDate)
65
+ return messageInfo
66
+ else:
67
+ return messageInfo, htmlBody
68
+
69
+
70
+ def selectFolderGetUids(imapObj, foldersName, text=None):
71
+ """
72
+ @param imapObj:邮件对象
73
+ @param foldersName: 文件夹
74
+ @param text: 搜索文本
75
+ @return: Uid,文件的id
76
+ """
77
+ imapObj.select_folder(foldersName, readonly=True)
78
+ # print(f"在 【{foldersName}】 中搜索关键词---> :{text}")
79
+ if text:
80
+ # 通过标题搜索
81
+ UIDs = imapObj.gmail_search(text)
82
+ else:
83
+ # 查找所有
84
+ UIDs = imapObj.gmail_search(u'All')
85
+ return UIDs
86
+
87
+
88
+ def loginShowFoldersEmail(email, password):
89
+ # 登录邮件
90
+ # imaplib._MAXLINE = 10000000 # 最大行数限制
91
+ context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
92
+ imapObj = imapclient.IMAPClient('imap.gmail.com', ssl=True, ssl_context=context)
93
+ imapObj.login(email, password)
94
+ folders = imapObj.list_folders()
95
+ print("所有的邮件文件夹:", [folder[2] for folder in folders])
96
+ return imapObj
97
+
98
+
99
+ def getTodayDateTime():
100
+ # 获取当天日期
101
+ since = datetime.utcnow() - timedelta(minutes=1)
102
+ receiveTime = since.strftime('%d-%b-%Y')
103
+ return receiveTime
104
+
105
+
106
+ def fetchEmail(imapObj, UIDs):
107
+ """
108
+ 标识为已读(打开邮件)
109
+ @param imapObj:
110
+ @param UIDs:
111
+ @return:
112
+ """
113
+ imapObj.fetch(UIDs, ['BODY[]'])
114
+ return True
115
+
116
+
117
+ def assertEmail(emailProperty=None, is_check_email_property=True):
118
+ errors = []
119
+ for temp in emailProperty:
120
+ _subject = list(temp.keys())[0]
121
+ # print("\n----执行遍历断言----> ", _subject)
122
+ try:
123
+ UIDs = selectFolderGetUids(imapObj, allEmail, _subject)
124
+ assert UIDs
125
+ except AssertionError:
126
+ UIDs = selectFolderGetUids(imapObj, junkEmail, _subject)
127
+ try:
128
+ assert UIDs
129
+ except AssertionError:
130
+ errors.append({_subject: "未收到邮件"})
131
+ is_check_email_property = False
132
+ # raise AssertionError(f"找不到相符的邮件内容:{_subject}")
133
+ finally:
134
+ # 断言回复邮箱和发件人
135
+ if is_check_email_property:
136
+ EmailContentAll = forParseEmailContent(UIDs)
137
+ property_result = assertEmailProperty(temp[_subject], EmailContentAll[_subject])
138
+ if len(property_result) > 0:
139
+ errors.append({_subject: property_result})
140
+ return [True, "邮件内容断言成功"] if len(errors) == 0 else [False, {"邮件内容断言失败": errors}]
141
+
142
+
143
+ def assertEmailProperty(emailProperty, ParseEmailContent):
144
+ """
145
+ 断言邮件内容
146
+ @param emailProperty:
147
+ @param ParseEmailContent:assertEmail
148
+ @return:
149
+ """
150
+ property_errors = []
151
+ # print("原始数据", ParseEmailContent)
152
+ if isinstance(ParseEmailContent, list):
153
+ _parseEmailContent = ParseEmailContent[0]
154
+ else:
155
+ # 新增-发件人、回复邮箱断言
156
+ _parseEmailContent = ParseEmailContent
157
+ if emailProperty.get('sender') != _parseEmailContent.get('sender'):
158
+ property_errors.append(
159
+ ["发件人断言失败", {"预期": emailProperty.get('sender'), "实际": _parseEmailContent.get('sender')}])
160
+ if emailProperty.get('receiveAddress') != _parseEmailContent.get('receiveAddress'):
161
+ property_errors.append(["回复邮箱断言失败",
162
+ {"预期": emailProperty.get('receiveAddress'),
163
+ "实际": _parseEmailContent.get('receiveAddress')}])
164
+ return property_errors
165
+
166
+
167
+ def getRandomHyperlinksInHtml(EmailContent):
168
+ """
169
+ 获取html中的随机超链接
170
+ @param html:
171
+ @return:
172
+ """
173
+ flag = 1
174
+ # page = BeautifulSoup(EmailContent[1], features="html.parser")
175
+ pages = BeautifulSoup(EmailContent[1], features="html.parser")
176
+ alinks = pages.findAll('a')
177
+ new_links = []
178
+ for url in alinks:
179
+ if url.get('href', None) != '#' and 'mailto:' not in url.get('href', None) and url.get('href',
180
+ None) is not None:
181
+ new_links.append(url.get('href', None))
182
+ # 随机拿一个
183
+ print(new_links)
184
+ url = new_links[random.randint(0, len(new_links) - 1)]
185
+ redirect_url = originalLinkGetsRedirectUrl(url)
186
+ # print("初始获取重定向后的链接:", redirect_url)
187
+ while 'unsubscribe' in redirect_url:
188
+ url = new_links[random.randint(0, len(new_links) - 1)]
189
+ redirect_url = originalLinkGetsRedirectUrl(url)
190
+ print("获取非退订包含utm链接:", originalLinkGetsRedirectUrl(url))
191
+ return url
192
+
193
+
194
+ def originalLinkGetsRedirectUrl(url):
195
+ """
196
+ 原始链接获取重定向后带有utm的链接
197
+ @param url:
198
+ @return:
199
+ """
200
+ global final_location_url
201
+ print(url)
202
+ try:
203
+ session = requests.session()
204
+ resp_1 = session.get(url, allow_redirects=True)
205
+ # print("第一次重定向:",resp_1.headers.get('Location'))
206
+ # location_url_1 = resp_1.headers.get('Location')
207
+ # resp_2 = session.get(location_url_1, allow_redirects=False)
208
+ # print("第二次重定向:",resp_2.headers.get('Location'))
209
+ # final_location_url = resp_2.headers.get('Location')
210
+ final_location_url = resp_1.url
211
+ return final_location_url
212
+ except:
213
+ raise
214
+
215
+
216
+ def parseUrlParameters(url):
217
+ """
218
+ 返回url后的拼接参数
219
+ @param url:
220
+ @return:
221
+ """
222
+ return parse_qs(requests.utils.urlparse(url).query)
223
+
224
+
225
+ def name_convert_to_snake(name: str) -> str:
226
+ """驼峰转下划线"""
227
+ if re.search(r'[^_][A-Z]', name):
228
+ name = re.sub(r'([^_])([A-Z][a-z]+)', r'\1_\2', name)
229
+ return name_convert_to_snake(name)
230
+ return name.lower()
231
+
232
+
233
+ def assertUtmConfig(emailProperty):
234
+ """
235
+ 断言utm
236
+ @param utmConfigInfo: 请求时存的utm参数
237
+ @param emailUtmConfig: 邮件内容获取的utm参数
238
+ @param activityId:
239
+ @return:
240
+ """
241
+ global EmailContentAll
242
+ for _email in emailProperty:
243
+ _subject = list(_email.keys())[0]
244
+ try:
245
+ UIDs = selectFolderGetUids(imapObj, allEmail, _subject)
246
+ assert UIDs
247
+ EmailContentAll = forParseEmailContent(UIDs, True)
248
+ except AssertionError:
249
+ UIDs = selectFolderGetUids(imapObj, junkEmail, _subject)
250
+ assert UIDs
251
+ EmailContentAll = forParseEmailContent(UIDs, True)
252
+ except Exception as e:
253
+ raise e
254
+ finally:
255
+ email = _email[_subject]
256
+ if email['utmConfigInfo'] is None:
257
+ print("--->测试邮件不进行utm校验")
258
+ break
259
+ temp = originalLinkGetsRedirectUrl(getRandomHyperlinksInHtml(EmailContentAll))
260
+ params = parseUrlParameters(temp)
261
+ for utmKey, utmValue in email['utmConfigInfo'].items():
262
+ assert params.get(name_convert_to_snake(utmKey))[0] == utmValue
263
+ # 断言sp参数
264
+ # ec1
265
+ if 'utm_term' in params:
266
+ utm_term = dict(parse.parse_qsl(params.get('utm_term')[0]))
267
+ _params = {**params, **utm_term}
268
+ params = _params
269
+ assert params.get('sp_medium') == 'email'
270
+ assert params.get('sp_source') == 'smartpush'
271
+ if email['activityId'] is not None:
272
+ assert params.get('sp_campaign') == str(email['activityId'])
273
+ # ec2
274
+ else:
275
+ assert params.get('sp_medium')[0] == 'email'
276
+ assert params.get('sp_source')[0] == 'smartpush'
277
+ if email['activityId'] is not None:
278
+ assert params.get('sp_campaign')[0] == str(email['activityId'])
279
+ # flow的断言需要另外写
280
+ print(f"UTM断言成功-->>{email['utmConfigInfo']} || {params} || {str(email['activityId'])}")
281
+
282
+
283
+ @retry(tries=3, delay=3, backoff=3, max_delay=20)
284
+ def check_email_content(emailProperty, loginEmail, password, **kwargs):
285
+ """
286
+ 校验邮件送达情况
287
+ loginEmail: 接收邮箱账号,必填
288
+ password: 邮箱密码,必填
289
+ emailProperty: list类型,格式: [{主旨: {receiveAddress: 回复邮箱, sender: 发件人}}], 必填
290
+
291
+ is_check_email_property: bool, 非必填,是否断言回复邮箱或发件人
292
+ is_check_utm: bool, 非必填,是否断言utm
293
+ """
294
+ global imapObj
295
+ try:
296
+ print("--->获取emailProperty变量", emailProperty)
297
+ imapObj = loginShowFoldersEmail(loginEmail, password)
298
+ result = assertEmail(emailProperty=emailProperty, **kwargs)
299
+ # assertUtmConfig(emailProperty)
300
+ return result
301
+ except Exception:
302
+ raise Exception
303
+ finally:
304
+ # 退出登录
305
+ imapObj.logout()
@@ -1,4 +1,4 @@
1
- def compare_lists(temp1, temp2, num=1):
1
+ def compare_lists(temp1, temp2, check_key=["completedCount"], all_key=False, num=1):
2
2
  """对比两个list中字典,a中字典对应的键值+num等于b字典中键值
3
3
  ab值示例:
4
4
  a = [{"123": {"a": 1, "b": 2}}, {"456": {"a": 5, "b": 6}}]
@@ -14,10 +14,14 @@ def compare_lists(temp1, temp2, num=1):
14
14
  # 获取内层字典
15
15
  inner_dict_a = temp1_a[outer_key]
16
16
  inner_dict_b = temp2_b[outer_key]
17
+ inner_dict_a_keys = list(inner_dict_a)
18
+ inner_dict_b_keys = list(inner_dict_b)
19
+ if all_key is False:
20
+ inner_dict_a_keys = inner_dict_b_keys = check_key
17
21
  # 遍历内层字典的键
18
- for inner_key in inner_dict_a:
22
+ for inner_key in inner_dict_a_keys:
19
23
  # 确保 temp2 对应的内层字典中也有相同的键
20
- if inner_key in inner_dict_b:
24
+ if inner_key in inner_dict_b_keys:
21
25
  # 检查是否满足条件
22
26
  if inner_dict_a[inner_key] + num != inner_dict_b[inner_key]:
23
27
  error.append({
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: smartpush
3
- Version: 1.3.8
3
+ Version: 1.3.9
4
4
  Summary: 用于smartpush自动化测试工具包
5
5
  Author: 卢泽彬、邵宇飞、周彦龙
6
6
 
@@ -1,18 +1,19 @@
1
1
  smartpush/__init__.py,sha256=XJrl1vhGATHSeSVqKmPXxYqxyseriUpvY5tLIXir3EE,24
2
2
  smartpush/get_jira_info.py,sha256=Ej2JRlO_Kct6Koju57b27ySy_B4q43f0I2bFcS_fmaw,17801
3
- smartpush/test.py,sha256=gOZE1am6-6ZPcfKLfh2KblHXhL5YY-LncPZhji4THYI,7648
3
+ smartpush/test.py,sha256=7akuB8EdegSOHr4M9ocfihU96yZqd_XeebFm6NkyuRE,34575
4
4
  smartpush/export/__init__.py,sha256=D9GbWcmwnetEndFDty5XbVienFK1WjqV2yYcQp3CM84,99
5
5
  smartpush/export/basic/ExcelExportChecker.py,sha256=R_1gFy69vE2V5pY20zFsnhqWgJ42Vtf35zBtOPfhUpU,17590
6
6
  smartpush/export/basic/GetOssUrl.py,sha256=LeF1y1_uJaYXth1KvO6mEDS29ezb9tliBv5SrbqYkXc,6136
7
7
  smartpush/export/basic/ReadExcel.py,sha256=ZnG2mtYqLY-xuYx9SyulbdYUP_0E5jIeKDewfakAsTw,7342
8
8
  smartpush/export/basic/__init__.py,sha256=6tcrS-2NSlsJo-UwEsnGUmwCf7jgOsh_UEbM0FD-gYE,70
9
- smartpush/flow/MockFlow.py,sha256=I9EKR9-rQ6H-fI4dzYZ_WRrHqxVdIcxaPKGKzG_3e3I,5083
9
+ smartpush/flow/MockFlow.py,sha256=O7-L3Jq0d_mO0REGxXgnyMH4FW7F0je2gS7dxFqIiDM,6338
10
10
  smartpush/flow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  smartpush/utils/DataTypeUtils.py,sha256=BC7ioztO3vAfKd1EOoNvXdVuXYY8qjNskV1DP7LhW-M,1082
12
- smartpush/utils/ListDictUtils.py,sha256=OsmNzMA3nCAUJaRw53uyUkZI0t95VBlfxYwD4FFtjz8,1385
12
+ smartpush/utils/EmailUtlis.py,sha256=DAHd73bJ8hiJCLEXtD0xcwxPD7SOPSmBB7Jvlf6gN6s,11201
13
+ smartpush/utils/ListDictUtils.py,sha256=qXxpMwWoyaXwOHhSNUt55HtFfwmqli-wZTWmiP9qhsI,1657
13
14
  smartpush/utils/StringUtils.py,sha256=n8mo9k0JQN63MReImgv-66JxmmymOGknR8pH2fkQrAo,4139
14
15
  smartpush/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- smartpush-1.3.8.dist-info/METADATA,sha256=8_OjDrX1Go1UcCFJO-VlEzcBHOlnIpVVmvxRiiRKCCs,145
16
- smartpush-1.3.8.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
17
- smartpush-1.3.8.dist-info/top_level.txt,sha256=5_CXqu08EfbPaKLjuSAOAqCmGU6shiatwDU_ViBGCmg,10
18
- smartpush-1.3.8.dist-info/RECORD,,
16
+ smartpush-1.3.9.dist-info/METADATA,sha256=M7O8bUQ3HKu1k5v70h9SzucE2fwVKv2cSN1DGtZHBHk,145
17
+ smartpush-1.3.9.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
18
+ smartpush-1.3.9.dist-info/top_level.txt,sha256=5_CXqu08EfbPaKLjuSAOAqCmGU6shiatwDU_ViBGCmg,10
19
+ smartpush-1.3.9.dist-info/RECORD,,