jackpieno

findPlayer

Sep 3rd, 2026
31
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.77 KB | None | 0 0
  1. import json
  2. import re
  3. import time
  4. import unicodedata
  5. from itertools import combinations
  6. from pathlib import Path
  7.  
  8.  
  9. # need pip install requests openpyxl
  10. import requests
  11. from openpyxl import Workbook
  12. from openpyxl.styles import Alignment, Font
  13.  
  14.  
  15. # ============================================================
  16. # Config
  17. # ============================================================
  18.  
  19. INPUT_FILE = "badAccount.txt"
  20. OUTPUT_XLSX = "mverse_analysis.xlsx"
  21. CACHE_FILE = "mverse_cache.json"
  22.  
  23. PROFILE_API = "https://mverse-api.nexon.com/social/v1/profile/{ppsn}"
  24. FRIENDS_API = "https://mverse-api.nexon.com/social/v1/{ppsn}/friends/all"
  25.  
  26. REQUEST_DELAY = 0
  27. TIMEOUT = 15
  28. MAX_RETRIES = 3
  29.  
  30. MIN_COLUMN_WIDTH = 10
  31. MAX_COLUMN_WIDTH = 50
  32. COLUMN_PADDING = 3
  33.  
  34.  
  35. session = requests.Session()
  36. session.headers.update({
  37. "User-Agent": "Mozilla/5.0",
  38. "Accept": "application/json",
  39. })
  40.  
  41.  
  42. # ============================================================
  43. # Input
  44. # ============================================================
  45.  
  46. def load_ppsns(filename):
  47. text = Path(filename).read_text(
  48. encoding="utf-8",
  49. errors="ignore",
  50. )
  51.  
  52. ppsns = re.findall(
  53. r"\b\d{15,20}\b",
  54. text,
  55. )
  56.  
  57. return list(dict.fromkeys(ppsns))
  58.  
  59.  
  60. # ============================================================
  61. # HTTP
  62. # ============================================================
  63.  
  64. def request_json(url):
  65. for attempt in range(1, MAX_RETRIES + 1):
  66. try:
  67. response = session.get(
  68. url,
  69. timeout=TIMEOUT,
  70. )
  71.  
  72. if response.status_code == 200:
  73. try:
  74. return response.json()
  75. except ValueError:
  76. return None
  77.  
  78. if response.status_code in {
  79. 400,
  80. 401,
  81. 403,
  82. 404,
  83. }:
  84. return None
  85.  
  86. except requests.RequestException:
  87. pass
  88.  
  89. if attempt < MAX_RETRIES:
  90. time.sleep(attempt)
  91.  
  92. return None
  93.  
  94.  
  95. # ============================================================
  96. # Generic JSON helper
  97. # ============================================================
  98.  
  99. def recursive_find(obj, key):
  100. if isinstance(obj, dict):
  101.  
  102. if key in obj:
  103. return obj[key]
  104.  
  105. for value in obj.values():
  106. result = recursive_find(
  107. value,
  108. key,
  109. )
  110.  
  111. if result is not None:
  112. return result
  113.  
  114. elif isinstance(obj, list):
  115.  
  116. for item in obj:
  117. result = recursive_find(
  118. item,
  119. key,
  120. )
  121.  
  122. if result is not None:
  123. return result
  124.  
  125. return None
  126.  
  127.  
  128. # ============================================================
  129. # Profile
  130. # ============================================================
  131.  
  132. def get_profile(ppsn):
  133. url = PROFILE_API.format(
  134. ppsn=ppsn
  135. )
  136.  
  137. data = request_json(url)
  138.  
  139. if not data:
  140. return {
  141. "ppsn": ppsn,
  142. "profileCode": "",
  143. "profileName": "",
  144. "status": "FAILED",
  145. "friendCount": 0,
  146. }
  147.  
  148. profile_code = (
  149. recursive_find(
  150. data,
  151. "profileCode",
  152. )
  153. or ""
  154. )
  155.  
  156. profile_name = (
  157. recursive_find(
  158. data,
  159. "profileName",
  160. )
  161. or recursive_find(
  162. data,
  163. "nickname",
  164. )
  165. or recursive_find(
  166. data,
  167. "characterName",
  168. )
  169. or recursive_find(
  170. data,
  171. "displayName",
  172. )
  173. or recursive_find(
  174. data,
  175. "name",
  176. )
  177. or ""
  178. )
  179.  
  180. return {
  181. "ppsn": str(ppsn),
  182. "profileCode": str(profile_code),
  183. "profileName": str(profile_name),
  184. "status": "OK",
  185. "friendCount": 0,
  186. }
  187.  
  188.  
  189. # ============================================================
  190. # Friends
  191. # ============================================================
  192.  
  193. def normalize_friend(friend):
  194. if not isinstance(friend, dict):
  195. return None
  196.  
  197. ppsn = str(
  198. friend.get("ppsn")
  199. or ""
  200. )
  201.  
  202. profile_code = str(
  203. friend.get("profileCode")
  204. or ""
  205. )
  206.  
  207. profile_name = str(
  208. friend.get("profileName")
  209. or ""
  210. )
  211.  
  212. if not ppsn:
  213. return None
  214.  
  215. return {
  216. "ppsn": ppsn,
  217. "profileCode": profile_code,
  218. "profileName": profile_name,
  219. }
  220.  
  221.  
  222. def get_friends(ppsn):
  223. url = FRIENDS_API.format(
  224. ppsn=ppsn
  225. )
  226.  
  227. data = request_json(url)
  228.  
  229. if not data:
  230. return {}
  231.  
  232. if data.get("code") != 0:
  233. return {}
  234.  
  235. api_data = data.get("data")
  236.  
  237. if not isinstance(
  238. api_data,
  239. dict,
  240. ):
  241. return {}
  242.  
  243. raw_friends = api_data.get(
  244. "result"
  245. )
  246.  
  247. if not isinstance(
  248. raw_friends,
  249. list,
  250. ):
  251. return {}
  252.  
  253. result = {}
  254.  
  255. for friend in raw_friends:
  256. normalized = normalize_friend(
  257. friend
  258. )
  259.  
  260. if not normalized:
  261. continue
  262.  
  263. friend_ppsn = normalized[
  264. "ppsn"
  265. ]
  266.  
  267. result[
  268. friend_ppsn
  269. ] = normalized
  270.  
  271. return result
  272.  
  273.  
  274. # ============================================================
  275. # Shared friends
  276. # ============================================================
  277.  
  278. def build_shared_friends(
  279. profiles_by_ppsn,
  280. friends_by_ppsn,
  281. ):
  282. rows = []
  283.  
  284. suspicious_ppsns = list(
  285. friends_by_ppsn.keys()
  286. )
  287.  
  288. for ppsn_a, ppsn_b in combinations(
  289. suspicious_ppsns,
  290. 2,
  291. ):
  292.  
  293. friends_a = friends_by_ppsn[
  294. ppsn_a
  295. ]
  296.  
  297. friends_b = friends_by_ppsn[
  298. ppsn_b
  299. ]
  300.  
  301. shared_ppsns = (
  302. set(friends_a.keys())
  303. &
  304. set(friends_b.keys())
  305. )
  306.  
  307. if not shared_ppsns:
  308. continue
  309.  
  310. profile_a = profiles_by_ppsn[
  311. ppsn_a
  312. ]
  313.  
  314. profile_b = profiles_by_ppsn[
  315. ppsn_b
  316. ]
  317.  
  318. shared_count = len(
  319. shared_ppsns
  320. )
  321.  
  322. for shared_ppsn in sorted(
  323. shared_ppsns
  324. ):
  325.  
  326. friend_a = friends_a[
  327. shared_ppsn
  328. ]
  329.  
  330. friend_b = friends_b[
  331. shared_ppsn
  332. ]
  333.  
  334. shared_profile_code = (
  335. friend_a["profileCode"]
  336. or friend_b["profileCode"]
  337. )
  338.  
  339. shared_profile_name = (
  340. friend_a["profileName"]
  341. or friend_b["profileName"]
  342. )
  343.  
  344. rows.append({
  345. "accountA_PPSN":
  346. ppsn_a,
  347.  
  348. "accountA_ProfileCode":
  349. profile_a[
  350. "profileCode"
  351. ],
  352.  
  353. "accountA_ProfileName":
  354. profile_a[
  355. "profileName"
  356. ],
  357.  
  358. "accountB_PPSN":
  359. ppsn_b,
  360.  
  361. "accountB_ProfileCode":
  362. profile_b[
  363. "profileCode"
  364. ],
  365.  
  366. "accountB_ProfileName":
  367. profile_b[
  368. "profileName"
  369. ],
  370.  
  371. "sharedFriend_PPSN":
  372. shared_ppsn,
  373.  
  374. "sharedFriend_ProfileCode":
  375. shared_profile_code,
  376.  
  377. "sharedFriend_ProfileName":
  378. shared_profile_name,
  379.  
  380. "sharedFriendCount":
  381. shared_count,
  382. })
  383.  
  384. rows.sort(
  385. key=lambda x: (
  386. -x[
  387. "sharedFriendCount"
  388. ],
  389. x[
  390. "accountA_ProfileCode"
  391. ],
  392. x[
  393. "accountB_ProfileCode"
  394. ],
  395. x[
  396. "sharedFriend_PPSN"
  397. ],
  398. )
  399. )
  400.  
  401. return rows
  402.  
  403.  
  404. # ============================================================
  405. # Pair summary
  406. # ============================================================
  407.  
  408. def build_pair_summary(
  409. shared_rows,
  410. ):
  411. pair_data = {}
  412.  
  413. for row in shared_rows:
  414.  
  415. key = (
  416. row[
  417. "accountA_PPSN"
  418. ],
  419. row[
  420. "accountB_PPSN"
  421. ],
  422. )
  423.  
  424. if key not in pair_data:
  425.  
  426. pair_data[key] = {
  427. "accountA_PPSN":
  428. row[
  429. "accountA_PPSN"
  430. ],
  431.  
  432. "accountA_ProfileCode":
  433. row[
  434. "accountA_ProfileCode"
  435. ],
  436.  
  437. "accountA_ProfileName":
  438. row[
  439. "accountA_ProfileName"
  440. ],
  441.  
  442. "accountB_PPSN":
  443. row[
  444. "accountB_PPSN"
  445. ],
  446.  
  447. "accountB_ProfileCode":
  448. row[
  449. "accountB_ProfileCode"
  450. ],
  451.  
  452. "accountB_ProfileName":
  453. row[
  454. "accountB_ProfileName"
  455. ],
  456.  
  457. "sharedFriendCount":
  458. row[
  459. "sharedFriendCount"
  460. ],
  461.  
  462. "sharedFriendPPSNs":
  463. [],
  464.  
  465. "sharedFriendProfileCodes":
  466. [],
  467.  
  468. "sharedFriendProfileNames":
  469. [],
  470. }
  471.  
  472. pair_data[key][
  473. "sharedFriendPPSNs"
  474. ].append(
  475. row[
  476. "sharedFriend_PPSN"
  477. ]
  478. )
  479.  
  480. pair_data[key][
  481. "sharedFriendProfileCodes"
  482. ].append(
  483. row[
  484. "sharedFriend_ProfileCode"
  485. ]
  486. )
  487.  
  488. pair_data[key][
  489. "sharedFriendProfileNames"
  490. ].append(
  491. row[
  492. "sharedFriend_ProfileName"
  493. ]
  494. )
  495.  
  496. rows = []
  497.  
  498. for data in pair_data.values():
  499.  
  500. rows.append({
  501. "accountA_PPSN":
  502. data[
  503. "accountA_PPSN"
  504. ],
  505.  
  506. "accountA_ProfileCode":
  507. data[
  508. "accountA_ProfileCode"
  509. ],
  510.  
  511. "accountA_ProfileName":
  512. data[
  513. "accountA_ProfileName"
  514. ],
  515.  
  516. "accountB_PPSN":
  517. data[
  518. "accountB_PPSN"
  519. ],
  520.  
  521. "accountB_ProfileCode":
  522. data[
  523. "accountB_ProfileCode"
  524. ],
  525.  
  526. "accountB_ProfileName":
  527. data[
  528. "accountB_ProfileName"
  529. ],
  530.  
  531. "sharedFriendCount":
  532. data[
  533. "sharedFriendCount"
  534. ],
  535.  
  536. "sharedFriendPPSNs":
  537. " | ".join(
  538. data[
  539. "sharedFriendPPSNs"
  540. ]
  541. ),
  542.  
  543. "sharedFriendProfileCodes":
  544. " | ".join(
  545. data[
  546. "sharedFriendProfileCodes"
  547. ]
  548. ),
  549.  
  550. "sharedFriendProfileNames":
  551. " | ".join(
  552. data[
  553. "sharedFriendProfileNames"
  554. ]
  555. ),
  556. })
  557.  
  558. rows.sort(
  559. key=lambda x: (
  560. -x[
  561. "sharedFriendCount"
  562. ],
  563. x[
  564. "accountA_ProfileCode"
  565. ],
  566. x[
  567. "accountB_ProfileCode"
  568. ],
  569. )
  570. )
  571.  
  572. return rows
  573.  
  574.  
  575. # ============================================================
  576. # Friend clusters
  577. # ============================================================
  578.  
  579. def build_friend_clusters(
  580. profiles_by_ppsn,
  581. friends_by_ppsn,
  582. ):
  583. friend_to_suspects = {}
  584. friend_info = {}
  585.  
  586. for suspect_ppsn, friends in (
  587. friends_by_ppsn.items()
  588. ):
  589.  
  590. for friend_ppsn, friend in (
  591. friends.items()
  592. ):
  593.  
  594. friend_to_suspects.setdefault(
  595. friend_ppsn,
  596. set(),
  597. )
  598.  
  599. friend_to_suspects[
  600. friend_ppsn
  601. ].add(
  602. suspect_ppsn
  603. )
  604.  
  605. if friend_ppsn not in friend_info:
  606.  
  607. friend_info[
  608. friend_ppsn
  609. ] = {
  610. "ppsn":
  611. friend_ppsn,
  612.  
  613. "profileCode":
  614. friend[
  615. "profileCode"
  616. ],
  617.  
  618. "profileName":
  619. friend[
  620. "profileName"
  621. ],
  622. }
  623.  
  624. else:
  625.  
  626. old = friend_info[
  627. friend_ppsn
  628. ]
  629.  
  630. if not old[
  631. "profileCode"
  632. ]:
  633. old[
  634. "profileCode"
  635. ] = friend[
  636. "profileCode"
  637. ]
  638.  
  639. if not old[
  640. "profileName"
  641. ]:
  642. old[
  643. "profileName"
  644. ] = friend[
  645. "profileName"
  646. ]
  647.  
  648. rows = []
  649.  
  650. for friend_ppsn, suspects in (
  651. friend_to_suspects.items()
  652. ):
  653.  
  654. if len(suspects) < 2:
  655. continue
  656.  
  657. friend = friend_info[
  658. friend_ppsn
  659. ]
  660.  
  661. suspect_info = []
  662.  
  663. for suspect_ppsn in sorted(
  664. suspects
  665. ):
  666.  
  667. profile = profiles_by_ppsn[
  668. suspect_ppsn
  669. ]
  670.  
  671. suspect_info.append({
  672. "ppsn":
  673. suspect_ppsn,
  674.  
  675. "profileCode":
  676. profile[
  677. "profileCode"
  678. ],
  679.  
  680. "profileName":
  681. profile[
  682. "profileName"
  683. ],
  684. })
  685.  
  686. rows.append({
  687. "friendPPSN":
  688. friend_ppsn,
  689.  
  690. "friendProfileCode":
  691. friend[
  692. "profileCode"
  693. ],
  694.  
  695. "friendProfileName":
  696. friend[
  697. "profileName"
  698. ],
  699.  
  700. "suspectCount":
  701. len(suspects),
  702.  
  703. "suspectPPSNs":
  704. " | ".join(
  705. x["ppsn"]
  706. for x
  707. in suspect_info
  708. ),
  709.  
  710. "suspectProfileCodes":
  711. " | ".join(
  712. x["profileCode"]
  713. for x
  714. in suspect_info
  715. ),
  716.  
  717. "suspectProfileNames":
  718. " | ".join(
  719. x["profileName"]
  720. for x
  721. in suspect_info
  722. ),
  723. })
  724.  
  725. rows.sort(
  726. key=lambda x: (
  727. -x[
  728. "suspectCount"
  729. ],
  730. x[
  731. "friendProfileCode"
  732. ],
  733. x[
  734. "friendPPSN"
  735. ],
  736. )
  737. )
  738.  
  739. return rows
  740.  
  741.  
  742. # ============================================================
  743. # Excel display width
  744. # ============================================================
  745.  
  746. def get_display_width(value):
  747. if value is None:
  748. return 0
  749.  
  750. text = str(value)
  751.  
  752. width = 0
  753.  
  754. for char in text:
  755.  
  756. east_asian_width = (
  757. unicodedata.east_asian_width(
  758. char
  759. )
  760. )
  761.  
  762. if east_asian_width in {
  763. "W",
  764. "F",
  765. }:
  766. width += 2
  767. else:
  768. width += 1
  769.  
  770. return width
  771.  
  772.  
  773. def auto_fit_worksheet(ws):
  774. for column_cells in ws.columns:
  775.  
  776. column_letter = (
  777. column_cells[
  778. 0
  779. ].column_letter
  780. )
  781.  
  782. max_length = 0
  783.  
  784. for cell in column_cells:
  785.  
  786. if cell.value is None:
  787. continue
  788.  
  789. value = str(
  790. cell.value
  791. )
  792.  
  793. lines = value.splitlines()
  794.  
  795. if not lines:
  796. lines = [""]
  797.  
  798. display_length = max(
  799. get_display_width(
  800. line
  801. )
  802. for line in lines
  803. )
  804.  
  805. max_length = max(
  806. max_length,
  807. display_length,
  808. )
  809.  
  810. wanted_width = (
  811. max_length
  812. + COLUMN_PADDING
  813. )
  814.  
  815. if wanted_width < MIN_COLUMN_WIDTH:
  816. wanted_width = (
  817. MIN_COLUMN_WIDTH
  818. )
  819.  
  820. if wanted_width > MAX_COLUMN_WIDTH:
  821.  
  822. ws.column_dimensions[
  823. column_letter
  824. ].width = (
  825. MAX_COLUMN_WIDTH
  826. )
  827.  
  828. for cell in column_cells:
  829.  
  830. cell.alignment = Alignment(
  831. wrap_text=True,
  832. vertical="top",
  833. )
  834.  
  835. else:
  836.  
  837. ws.column_dimensions[
  838. column_letter
  839. ].width = (
  840. wanted_width
  841. )
  842.  
  843.  
  844. # ============================================================
  845. # Excel helpers
  846. # ============================================================
  847.  
  848. def format_sheet(ws):
  849. for cell in ws[1]:
  850. cell.font = Font(
  851. bold=True
  852. )
  853.  
  854. cell.alignment = Alignment(
  855. vertical="center",
  856. )
  857.  
  858. ws.freeze_panes = "A2"
  859.  
  860. ws.auto_filter.ref = (
  861. ws.dimensions
  862. )
  863.  
  864. auto_fit_worksheet(
  865. ws
  866. )
  867.  
  868.  
  869. def force_text(
  870. ws,
  871. row,
  872. columns,
  873. ):
  874. for column in columns:
  875.  
  876. cell = ws.cell(
  877. row=row,
  878. column=column,
  879. )
  880.  
  881. cell.number_format = "@"
  882.  
  883.  
  884. # ============================================================
  885. # Excel output
  886. # ============================================================
  887.  
  888. def write_xlsx(
  889. profiles_by_ppsn,
  890. friends_by_ppsn,
  891. shared_rows,
  892. pair_rows,
  893. cluster_rows,
  894. ):
  895. wb = Workbook()
  896.  
  897. # --------------------------------------------------------
  898. # Profiles
  899. # --------------------------------------------------------
  900.  
  901. ws = wb.active
  902. ws.title = "Profiles"
  903.  
  904. ws.append([
  905. "PPSN",
  906. "ProfileCode",
  907. "ProfileName",
  908. "FriendCount",
  909. "Status",
  910. ])
  911.  
  912. profiles = list(
  913. profiles_by_ppsn.values()
  914. )
  915.  
  916. profiles.sort(
  917. key=lambda x: (
  918. x["profileCode"] == "",
  919. x["profileCode"],
  920. x["ppsn"],
  921. )
  922. )
  923.  
  924. for profile in profiles:
  925.  
  926. ws.append([
  927. str(
  928. profile[
  929. "ppsn"
  930. ]
  931. ),
  932.  
  933. profile[
  934. "profileCode"
  935. ],
  936.  
  937. profile[
  938. "profileName"
  939. ],
  940.  
  941. profile[
  942. "friendCount"
  943. ],
  944.  
  945. profile[
  946. "status"
  947. ],
  948. ])
  949.  
  950. force_text(
  951. ws,
  952. ws.max_row,
  953. [1],
  954. )
  955.  
  956. format_sheet(ws)
  957.  
  958. # --------------------------------------------------------
  959. # All Friends
  960. # --------------------------------------------------------
  961.  
  962. ws = wb.create_sheet(
  963. "All Friends"
  964. )
  965.  
  966. ws.append([
  967. "Owner PPSN",
  968. "Owner ProfileCode",
  969. "Owner ProfileName",
  970.  
  971. "Friend PPSN",
  972. "Friend ProfileCode",
  973. "Friend ProfileName",
  974. ])
  975.  
  976. all_friend_rows = []
  977.  
  978. for owner_ppsn, friends in (
  979. friends_by_ppsn.items()
  980. ):
  981.  
  982. owner = profiles_by_ppsn[
  983. owner_ppsn
  984. ]
  985.  
  986. for friend_ppsn, friend in (
  987. friends.items()
  988. ):
  989.  
  990. all_friend_rows.append({
  991. "ownerPPSN":
  992. owner_ppsn,
  993.  
  994. "ownerProfileCode":
  995. owner[
  996. "profileCode"
  997. ],
  998.  
  999. "ownerProfileName":
  1000. owner[
  1001. "profileName"
  1002. ],
  1003.  
  1004. "friendPPSN":
  1005. friend_ppsn,
  1006.  
  1007. "friendProfileCode":
  1008. friend[
  1009. "profileCode"
  1010. ],
  1011.  
  1012. "friendProfileName":
  1013. friend[
  1014. "profileName"
  1015. ],
  1016. })
  1017.  
  1018. all_friend_rows.sort(
  1019. key=lambda x: (
  1020. x[
  1021. "ownerProfileCode"
  1022. ] == "",
  1023. x[
  1024. "ownerProfileCode"
  1025. ],
  1026. x[
  1027. "ownerPPSN"
  1028. ],
  1029. x[
  1030. "friendProfileCode"
  1031. ],
  1032. x[
  1033. "friendPPSN"
  1034. ],
  1035. )
  1036. )
  1037.  
  1038. for item in all_friend_rows:
  1039.  
  1040. ws.append([
  1041. str(
  1042. item[
  1043. "ownerPPSN"
  1044. ]
  1045. ),
  1046.  
  1047. item[
  1048. "ownerProfileCode"
  1049. ],
  1050.  
  1051. item[
  1052. "ownerProfileName"
  1053. ],
  1054.  
  1055. str(
  1056. item[
  1057. "friendPPSN"
  1058. ]
  1059. ),
  1060.  
  1061. item[
  1062. "friendProfileCode"
  1063. ],
  1064.  
  1065. item[
  1066. "friendProfileName"
  1067. ],
  1068. ])
  1069.  
  1070. force_text(
  1071. ws,
  1072. ws.max_row,
  1073. [1, 4],
  1074. )
  1075.  
  1076. format_sheet(ws)
  1077.  
  1078. # --------------------------------------------------------
  1079. # Shared Friends
  1080. # --------------------------------------------------------
  1081.  
  1082. ws = wb.create_sheet(
  1083. "Shared Friends"
  1084. )
  1085.  
  1086. ws.append([
  1087. "Account A PPSN",
  1088. "Account A ProfileCode",
  1089. "Account A ProfileName",
  1090.  
  1091. "Account B PPSN",
  1092. "Account B ProfileCode",
  1093. "Account B ProfileName",
  1094.  
  1095. "Shared Friend PPSN",
  1096. "Shared Friend ProfileCode",
  1097. "Shared Friend ProfileName",
  1098.  
  1099. "Shared Friend Count",
  1100. ])
  1101.  
  1102. for item in shared_rows:
  1103.  
  1104. ws.append([
  1105. str(
  1106. item[
  1107. "accountA_PPSN"
  1108. ]
  1109. ),
  1110.  
  1111. item[
  1112. "accountA_ProfileCode"
  1113. ],
  1114.  
  1115. item[
  1116. "accountA_ProfileName"
  1117. ],
  1118.  
  1119. str(
  1120. item[
  1121. "accountB_PPSN"
  1122. ]
  1123. ),
  1124.  
  1125. item[
  1126. "accountB_ProfileCode"
  1127. ],
  1128.  
  1129. item[
  1130. "accountB_ProfileName"
  1131. ],
  1132.  
  1133. str(
  1134. item[
  1135. "sharedFriend_PPSN"
  1136. ]
  1137. ),
  1138.  
  1139. item[
  1140. "sharedFriend_ProfileCode"
  1141. ],
  1142.  
  1143. item[
  1144. "sharedFriend_ProfileName"
  1145. ],
  1146.  
  1147. item[
  1148. "sharedFriendCount"
  1149. ],
  1150. ])
  1151.  
  1152. force_text(
  1153. ws,
  1154. ws.max_row,
  1155. [1, 4, 7],
  1156. )
  1157.  
  1158. format_sheet(ws)
  1159.  
  1160. # --------------------------------------------------------
  1161. # Pair Summary
  1162. # --------------------------------------------------------
  1163.  
  1164. ws = wb.create_sheet(
  1165. "Pair Summary"
  1166. )
  1167.  
  1168. ws.append([
  1169. "Account A PPSN",
  1170. "Account A ProfileCode",
  1171. "Account A ProfileName",
  1172.  
  1173. "Account B PPSN",
  1174. "Account B ProfileCode",
  1175. "Account B ProfileName",
  1176.  
  1177. "Shared Friend Count",
  1178.  
  1179. "Shared Friend PPSNs",
  1180. "Shared Friend ProfileCodes",
  1181. "Shared Friend ProfileNames",
  1182. ])
  1183.  
  1184. for item in pair_rows:
  1185.  
  1186. ws.append([
  1187. str(
  1188. item[
  1189. "accountA_PPSN"
  1190. ]
  1191. ),
  1192.  
  1193. item[
  1194. "accountA_ProfileCode"
  1195. ],
  1196.  
  1197. item[
  1198. "accountA_ProfileName"
  1199. ],
  1200.  
  1201. str(
  1202. item[
  1203. "accountB_PPSN"
  1204. ]
  1205. ),
  1206.  
  1207. item[
  1208. "accountB_ProfileCode"
  1209. ],
  1210.  
  1211. item[
  1212. "accountB_ProfileName"
  1213. ],
  1214.  
  1215. item[
  1216. "sharedFriendCount"
  1217. ],
  1218.  
  1219. item[
  1220. "sharedFriendPPSNs"
  1221. ],
  1222.  
  1223. item[
  1224. "sharedFriendProfileCodes"
  1225. ],
  1226.  
  1227. item[
  1228. "sharedFriendProfileNames"
  1229. ],
  1230. ])
  1231.  
  1232. force_text(
  1233. ws,
  1234. ws.max_row,
  1235. [1, 4, 8],
  1236. )
  1237.  
  1238. format_sheet(ws)
  1239.  
  1240. # --------------------------------------------------------
  1241. # Friend Clusters
  1242. # --------------------------------------------------------
  1243.  
  1244. ws = wb.create_sheet(
  1245. "Friend Clusters"
  1246. )
  1247.  
  1248. ws.append([
  1249. "Friend PPSN",
  1250. "Friend ProfileCode",
  1251. "Friend ProfileName",
  1252.  
  1253. "Suspect Count",
  1254.  
  1255. "Suspect PPSNs",
  1256. "Suspect ProfileCodes",
  1257. "Suspect ProfileNames",
  1258. ])
  1259.  
  1260. for item in cluster_rows:
  1261.  
  1262. ws.append([
  1263. str(
  1264. item[
  1265. "friendPPSN"
  1266. ]
  1267. ),
  1268.  
  1269. item[
  1270. "friendProfileCode"
  1271. ],
  1272.  
  1273. item[
  1274. "friendProfileName"
  1275. ],
  1276.  
  1277. item[
  1278. "suspectCount"
  1279. ],
  1280.  
  1281. item[
  1282. "suspectPPSNs"
  1283. ],
  1284.  
  1285. item[
  1286. "suspectProfileCodes"
  1287. ],
  1288.  
  1289. item[
  1290. "suspectProfileNames"
  1291. ],
  1292. ])
  1293.  
  1294. force_text(
  1295. ws,
  1296. ws.max_row,
  1297. [1, 5],
  1298. )
  1299.  
  1300. format_sheet(ws)
  1301.  
  1302. wb.save(
  1303. OUTPUT_XLSX
  1304. )
  1305.  
  1306.  
  1307. # ============================================================
  1308. # Cache
  1309. # ============================================================
  1310.  
  1311. def save_cache(
  1312. profiles_by_ppsn,
  1313. friends_by_ppsn,
  1314. ):
  1315. cache = {
  1316. "profiles":
  1317. profiles_by_ppsn,
  1318.  
  1319. "friends":
  1320. friends_by_ppsn,
  1321. }
  1322.  
  1323. with open(
  1324. CACHE_FILE,
  1325. "w",
  1326. encoding="utf-8",
  1327. ) as f:
  1328.  
  1329. json.dump(
  1330. cache,
  1331. f,
  1332. ensure_ascii=False,
  1333. indent=2,
  1334. )
  1335.  
  1336.  
  1337. # ============================================================
  1338. # Main
  1339. # ============================================================
  1340.  
  1341. def main():
  1342. ppsns = load_ppsns(
  1343. INPUT_FILE
  1344. )
  1345.  
  1346. if not ppsns:
  1347. return
  1348.  
  1349. profiles_by_ppsn = {}
  1350. friends_by_ppsn = {}
  1351.  
  1352. for ppsn in ppsns:
  1353.  
  1354. profile = get_profile(
  1355. ppsn
  1356. )
  1357.  
  1358. profiles_by_ppsn[
  1359. ppsn
  1360. ] = profile
  1361.  
  1362. time.sleep(
  1363. REQUEST_DELAY
  1364. )
  1365.  
  1366. for ppsn in ppsns:
  1367.  
  1368. profile = profiles_by_ppsn[
  1369. ppsn
  1370. ]
  1371.  
  1372. friends = get_friends(
  1373. ppsn
  1374. )
  1375.  
  1376. friends_by_ppsn[
  1377. ppsn
  1378. ] = friends
  1379.  
  1380. profile[
  1381. "friendCount"
  1382. ] = len(
  1383. friends
  1384. )
  1385.  
  1386. time.sleep(
  1387. REQUEST_DELAY
  1388. )
  1389.  
  1390. shared_rows = (
  1391. build_shared_friends(
  1392. profiles_by_ppsn,
  1393. friends_by_ppsn,
  1394. )
  1395. )
  1396.  
  1397. pair_rows = (
  1398. build_pair_summary(
  1399. shared_rows
  1400. )
  1401. )
  1402.  
  1403. cluster_rows = (
  1404. build_friend_clusters(
  1405. profiles_by_ppsn,
  1406. friends_by_ppsn,
  1407. )
  1408. )
  1409.  
  1410. write_xlsx(
  1411. profiles_by_ppsn,
  1412. friends_by_ppsn,
  1413. shared_rows,
  1414. pair_rows,
  1415. cluster_rows,
  1416. )
  1417.  
  1418. save_cache(
  1419. profiles_by_ppsn,
  1420. friends_by_ppsn,
  1421. )
  1422.  
  1423.  
  1424. if __name__ == "__main__":
  1425. main()
  1426. print("done!")
  1427.  
Advertisement
Add Comment
Please, Sign In to add comment