comdoleger commited on
Commit
744e37c
·
verified ·
1 Parent(s): 25bce54

Upload scripts/update_sponsors.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/update_sponsors.py +309 -0
scripts/update_sponsors.py ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import json
4
+ from datetime import datetime
5
+ from dotenv import load_dotenv
6
+
7
+ # Load environment variables from .env file
8
+ env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env")
9
+ load_dotenv(dotenv_path=env_path)
10
+
11
+ # API credentials
12
+ PATREON_TOKEN = os.getenv("PATREON_ACCESS_TOKEN")
13
+ GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
14
+ GITHUB_USERNAME = os.getenv("GITHUB_USERNAME")
15
+ GITHUB_ORG = os.getenv("GITHUB_ORG") # Organization name (optional)
16
+
17
+ # Output file
18
+ README_PATH = "SUPPORTERS.md"
19
+
20
+ def fetch_patreon_supporters():
21
+ """Fetch current Patreon supporters"""
22
+ print("Fetching Patreon supporters...")
23
+
24
+ headers = {
25
+ "Authorization": f"Bearer {PATREON_TOKEN}",
26
+ "Content-Type": "application/json"
27
+ }
28
+
29
+ url = "https://www.patreon.com/api/oauth2/v2/campaigns"
30
+
31
+ try:
32
+ # First get the campaign ID
33
+ campaign_response = requests.get(url, headers=headers)
34
+ campaign_response.raise_for_status()
35
+ campaign_data = campaign_response.json()
36
+
37
+ if not campaign_data.get('data'):
38
+ print("No campaigns found for this Patreon account")
39
+ return []
40
+
41
+ campaign_id = campaign_data['data'][0]['id']
42
+
43
+ # Now get the supporters for this campaign
44
+ members_url = f"https://www.patreon.com/api/oauth2/v2/campaigns/{campaign_id}/members"
45
+ params = {
46
+ "include": "user",
47
+ "fields[member]": "full_name,is_follower,patron_status", # Removed profile_url
48
+ "fields[user]": "image_url"
49
+ }
50
+
51
+ supporters = []
52
+ while members_url:
53
+ members_response = requests.get(members_url, headers=headers, params=params)
54
+ members_response.raise_for_status()
55
+ members_data = members_response.json()
56
+
57
+ # Process the response to extract active patrons
58
+ for member in members_data.get('data', []):
59
+ attributes = member.get('attributes', {})
60
+
61
+ # Only include active patrons
62
+ if attributes.get('patron_status') == 'active_patron':
63
+ name = attributes.get('full_name', 'Anonymous Supporter')
64
+
65
+ # Get user data which contains the profile image
66
+ user_id = member.get('relationships', {}).get('user', {}).get('data', {}).get('id')
67
+ profile_image = None
68
+ profile_url = None # Removed profile_url since it's not supported
69
+
70
+ if user_id:
71
+ for included in members_data.get('included', []):
72
+ if included.get('id') == user_id and included.get('type') == 'user':
73
+ profile_image = included.get('attributes', {}).get('image_url')
74
+ break
75
+
76
+ supporters.append({
77
+ 'name': name,
78
+ 'profile_image': profile_image,
79
+ 'profile_url': profile_url, # This will be None
80
+ 'platform': 'Patreon',
81
+ 'amount': 0 # Placeholder, as Patreon API doesn't provide this in the current response
82
+ })
83
+
84
+ # Handle pagination
85
+ members_url = members_data.get('links', {}).get('next')
86
+
87
+ print(f"Found {len(supporters)} active Patreon supporters")
88
+ return supporters
89
+
90
+ except requests.exceptions.RequestException as e:
91
+ print(f"Error fetching Patreon data: {e}")
92
+ print(f"Response content: {e.response.content if hasattr(e, 'response') else 'No response content'}")
93
+ return []
94
+
95
+ def fetch_github_sponsors():
96
+ """Fetch current GitHub sponsors for a user or organization"""
97
+ print("Fetching GitHub sponsors...")
98
+
99
+ headers = {
100
+ "Authorization": f"Bearer {GITHUB_TOKEN}",
101
+ "Accept": "application/vnd.github.v3+json"
102
+ }
103
+
104
+ # Determine if we're fetching for a user or an organization
105
+ entity_type = "organization" if GITHUB_ORG else "user"
106
+ entity_name = GITHUB_ORG if GITHUB_ORG else GITHUB_USERNAME
107
+
108
+ if not entity_name:
109
+ print("Error: Neither GITHUB_USERNAME nor GITHUB_ORG is set")
110
+ return []
111
+
112
+ # Different GraphQL query structure based on entity type
113
+ if entity_type == "user":
114
+ query = """
115
+ query {
116
+ user(login: "%s") {
117
+ sponsorshipsAsMaintainer(first: 100) {
118
+ nodes {
119
+ sponsorEntity {
120
+ ... on User {
121
+ login
122
+ name
123
+ avatarUrl
124
+ url
125
+ }
126
+ ... on Organization {
127
+ login
128
+ name
129
+ avatarUrl
130
+ url
131
+ }
132
+ }
133
+ tier {
134
+ monthlyPriceInDollars
135
+ }
136
+ isOneTimePayment
137
+ isActive
138
+ }
139
+ }
140
+ }
141
+ }
142
+ """ % entity_name
143
+ else: # organization
144
+ query = """
145
+ query {
146
+ organization(login: "%s") {
147
+ sponsorshipsAsMaintainer(first: 100) {
148
+ nodes {
149
+ sponsorEntity {
150
+ ... on User {
151
+ login
152
+ name
153
+ avatarUrl
154
+ url
155
+ }
156
+ ... on Organization {
157
+ login
158
+ name
159
+ avatarUrl
160
+ url
161
+ }
162
+ }
163
+ tier {
164
+ monthlyPriceInDollars
165
+ }
166
+ isOneTimePayment
167
+ isActive
168
+ }
169
+ }
170
+ }
171
+ }
172
+ """ % entity_name
173
+
174
+ try:
175
+ response = requests.post(
176
+ "https://api.github.com/graphql",
177
+ headers=headers,
178
+ json={"query": query}
179
+ )
180
+ response.raise_for_status()
181
+ data = response.json()
182
+
183
+ # Process the response - the path to the data differs based on entity type
184
+ if entity_type == "user":
185
+ sponsors_data = data.get('data', {}).get('user', {}).get('sponsorshipsAsMaintainer', {}).get('nodes', [])
186
+ else:
187
+ sponsors_data = data.get('data', {}).get('organization', {}).get('sponsorshipsAsMaintainer', {}).get('nodes', [])
188
+
189
+ sponsors = []
190
+ for sponsor in sponsors_data:
191
+ # Only include active sponsors
192
+ if sponsor.get('isActive'):
193
+ entity = sponsor.get('sponsorEntity', {})
194
+ name = entity.get('name') or entity.get('login', 'Anonymous Sponsor')
195
+ profile_image = entity.get('avatarUrl')
196
+ profile_url = entity.get('url')
197
+ amount = sponsor.get('tier', {}).get('monthlyPriceInDollars', 0)
198
+
199
+ sponsors.append({
200
+ 'name': name,
201
+ 'profile_image': profile_image,
202
+ 'profile_url': profile_url,
203
+ 'platform': 'GitHub Sponsors',
204
+ 'amount': amount
205
+ })
206
+
207
+ print(f"Found {len(sponsors)} active GitHub sponsors for {entity_type} '{entity_name}'")
208
+ return sponsors
209
+
210
+ except requests.exceptions.RequestException as e:
211
+ print(f"Error fetching GitHub sponsors data: {e}")
212
+ return []
213
+
214
+ def generate_readme(supporters):
215
+ """Generate a README.md file with supporter information"""
216
+ print(f"Generating {README_PATH}...")
217
+
218
+ # Sort supporters by amount (descending) and then by name
219
+ supporters.sort(key=lambda x: (-x['amount'], x['name'].lower()))
220
+
221
+ # Determine the proper footer links based on what's configured
222
+ github_entity = GITHUB_ORG if GITHUB_ORG else GITHUB_USERNAME
223
+ github_entity_type = "orgs" if GITHUB_ORG else "sponsors"
224
+ github_sponsor_url = f"https://github.com/{github_entity_type}/{github_entity}"
225
+
226
+ with open(README_PATH, "w", encoding="utf-8") as f:
227
+ f.write("## Support My Work\n\n")
228
+ f.write("If you enjoy my work, or use it for commercial purposes, please consider sponsoring me so I can continue to maintain it. Every bit helps! \n\n")
229
+ # Create appropriate call-to-action based on what's configured
230
+ cta_parts = []
231
+ if github_entity:
232
+ cta_parts.append(f"[Become a sponsor on GitHub]({github_sponsor_url})")
233
+ if PATREON_TOKEN:
234
+ cta_parts.append("[support me on Patreon](https://www.patreon.com/ostris)")
235
+
236
+ if cta_parts:
237
+ if GITHUB_ORG:
238
+ f.write(f"{' or '.join(cta_parts)}.\n\n")
239
+ f.write("Thank you to all my current supporters!\n\n")
240
+
241
+ f.write(f"_Last updated: {datetime.now().strftime('%Y-%m-%d')}_\n\n")
242
+
243
+ # Write GitHub Sponsors section
244
+ github_sponsors = [s for s in supporters if s['platform'] == 'GitHub Sponsors']
245
+ if github_sponsors:
246
+ f.write("### GitHub Sponsors\n\n")
247
+ for sponsor in github_sponsors:
248
+ if sponsor['profile_image']:
249
+ f.write(f"<a href=\"{sponsor['profile_url']}\" title=\"{sponsor['name']}\"><img src=\"{sponsor['profile_image']}\" width=\"50\" height=\"50\" alt=\"{sponsor['name']}\" style=\"border-radius:50%;display:inline-block;\"></a> ")
250
+ else:
251
+ f.write(f"[{sponsor['name']}]({sponsor['profile_url']}) ")
252
+ f.write("\n\n")
253
+
254
+ # Write Patreon section
255
+ patreon_supporters = [s for s in supporters if s['platform'] == 'Patreon']
256
+ if patreon_supporters:
257
+ f.write("### Patreon Supporters\n\n")
258
+ for supporter in patreon_supporters:
259
+ if supporter['profile_image']:
260
+ f.write(f"<a href=\"{supporter['profile_url']}\" title=\"{supporter['name']}\"><img src=\"{supporter['profile_image']}\" width=\"50\" height=\"50\" alt=\"{supporter['name']}\" style=\"border-radius:50%;display:inline-block;\"></a> ")
261
+ else:
262
+ f.write(f"[{supporter['name']}]({supporter['profile_url']}) ")
263
+ f.write("\n\n")
264
+
265
+ f.write("\n---\n\n")
266
+
267
+
268
+ print(f"Successfully generated {README_PATH} with {len(supporters)} supporters!")
269
+
270
+ def main():
271
+ """Main function"""
272
+ print("Starting supporter data collection...")
273
+
274
+ # Check if required environment variables are set
275
+ missing_vars = []
276
+ if not GITHUB_TOKEN:
277
+ missing_vars.append("GITHUB_TOKEN")
278
+
279
+ # Either username or org is required for GitHub
280
+ if not GITHUB_USERNAME and not GITHUB_ORG:
281
+ missing_vars.append("GITHUB_USERNAME or GITHUB_ORG")
282
+
283
+ # Patreon token is optional but warn if missing
284
+ patreon_enabled = bool(PATREON_TOKEN)
285
+
286
+ if missing_vars:
287
+ print(f"Error: Missing required environment variables: {', '.join(missing_vars)}")
288
+ print("Please add them to your .env file")
289
+ return
290
+
291
+ if not patreon_enabled:
292
+ print("Warning: PATREON_ACCESS_TOKEN not set. Will only fetch GitHub sponsors.")
293
+
294
+ # Fetch data from both platforms
295
+ patreon_supporters = fetch_patreon_supporters() if PATREON_TOKEN else []
296
+ github_sponsors = fetch_github_sponsors()
297
+
298
+ # Combine supporters from both platforms
299
+ all_supporters = patreon_supporters + github_sponsors
300
+
301
+ if not all_supporters:
302
+ print("No supporters found on either platform")
303
+ return
304
+
305
+ # Generate README
306
+ generate_readme(all_supporters)
307
+
308
+ if __name__ == "__main__":
309
+ main()