tumblrbot 1.5.0__tar.gz → 1.6.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tumblrbot
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: An updated bot that posts to Tumblr, based on your very own blog!
5
5
  Requires-Python: >= 3.13
6
6
  Description-Content-Type: text/markdown
@@ -73,14 +73,14 @@ Features:
73
73
  1. [Generates and uploads posts][Generate] to the [configured][config] [Tumblr] blog using the [configured][config] fine-tuned model.
74
74
  - Creates tags by extracting keywords at the [configured][config] frequency using the [configured][config] model.
75
75
  - Uploads posts as drafts to the [configured][config] [Tumblr] blog.
76
+ - Reblog posts at the [configured][config] frequency.
76
77
  - Shows progress and previews the current post.
77
78
  - Colorful output, progress bars, and post previews using [rich].
78
79
  - Automatically keeps the [config] file up-to-date and recreates it if missing.
79
80
 
80
81
  **To-Do:**
81
82
 
82
- - Add code documentation.
83
- - Add reblog generation logic.
83
+ - ...
84
84
 
85
85
  **Known Issues:**
86
86
 
@@ -55,14 +55,14 @@ Features:
55
55
  1. [Generates and uploads posts][Generate] to the [configured][config] [Tumblr] blog using the [configured][config] fine-tuned model.
56
56
  - Creates tags by extracting keywords at the [configured][config] frequency using the [configured][config] model.
57
57
  - Uploads posts as drafts to the [configured][config] [Tumblr] blog.
58
+ - Reblog posts at the [configured][config] frequency.
58
59
  - Shows progress and previews the current post.
59
60
  - Colorful output, progress bars, and post previews using [rich].
60
61
  - Automatically keeps the [config] file up-to-date and recreates it if missing.
61
62
 
62
63
  **To-Do:**
63
64
 
64
- - Add code documentation.
65
- - Add reblog generation logic.
65
+ - ...
66
66
 
67
67
  **Known Issues:**
68
68
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tumblrbot"
3
- version = "1.5.0"
3
+ version = "1.6.0"
4
4
  description = "An updated bot that posts to Tumblr, based on your very own blog!"
5
5
  readme = "README.md"
6
6
  requires-python = ">= 3.13"
@@ -1,4 +1,4 @@
1
- from random import random
1
+ from random import random, randrange
2
2
  from typing import override
3
3
 
4
4
  import rich
@@ -28,18 +28,28 @@ class DraftGenerator(FlowClass):
28
28
  rich.print(f":chart_increasing: [bold green]Generated {self.config.draft_count} draft(s).[/] {message}")
29
29
 
30
30
  def generate_post(self) -> Post:
31
- text = self.generate_text()
31
+ if random() < self.config.reblog_chance: # noqa: S311
32
+ original = self.get_random_post()
33
+ user_message = f"{self.config.reblog_user_message}\n\n{original.get_content_text()}"
34
+ else:
35
+ original = Post()
36
+ user_message = self.config.user_message
37
+
38
+ text = self.generate_text(user_message)
32
39
  if tags := self.generate_tags(text):
33
40
  tags = tags.tags
34
41
  return Post(
35
42
  content=[Post.Block(type="text", text=text)],
36
43
  tags=tags or [],
37
44
  state="draft",
45
+ parent_tumblelog_uuid=original.blog.uuid,
46
+ parent_post_id=original.id,
47
+ reblog_key=original.reblog_key,
38
48
  )
39
49
 
40
- def generate_text(self) -> str:
50
+ def generate_text(self, user_message: str) -> str:
41
51
  return self.openai.responses.create(
42
- input=self.config.user_message,
52
+ input=user_message,
43
53
  instructions=self.config.developer_message,
44
54
  model=self.config.fine_tuned_model,
45
55
  ).output_text
@@ -54,3 +64,11 @@ class DraftGenerator(FlowClass):
54
64
  ).output_parsed
55
65
 
56
66
  return None
67
+
68
+ def get_random_post(self) -> Post:
69
+ total = self.tumblr.retrieve_blog_info(self.config.upload_blog_identifier).response.blog.posts
70
+ post = self.tumblr.retrieve_published_posts(
71
+ self.config.upload_blog_identifier,
72
+ offset=randrange(total), # noqa: S311
73
+ ).response.posts[0]
74
+ return Post.model_validate(post)
@@ -74,6 +74,8 @@ class Config(FileSyncSettings):
74
74
  draft_count: PositiveInt = Field(150, description="The number of drafts to process. This will affect the number of tokens used with OpenAI")
75
75
  tags_chance: NonNegativeFloat = Field(0.1, description="The chance to generate tags for any given post. This will incur extra calls to OpenAI.")
76
76
  tags_developer_message: str = Field("You will be provided with a block of text, and your task is to extract a very short list of the most important subjects from it.", description="The developer message used to generate tags.")
77
+ reblog_chance: NonNegativeFloat = Field(0.05, description="The chance to generate a reblog of a random post.")
78
+ reblog_user_message: str = Field("Please write a comical Tumblr post in response to the following Tumblr post:", description="The prefix for the user message used to reblog posts.")
77
79
 
78
80
  @classmethod
79
81
  @override
@@ -100,7 +102,7 @@ class Config(FileSyncSettings):
100
102
 
101
103
  toml_table[name] = value
102
104
 
103
- Path(self.toml_file).write_text(tomlkit.dumps(toml_table), encoding="utf_8")
105
+ self.toml_file.write_text(tomlkit.dumps(toml_table), encoding="utf_8")
104
106
 
105
107
  return self
106
108
 
@@ -170,12 +172,14 @@ class Tokens(FileSyncSettings):
170
172
  return self
171
173
 
172
174
 
175
+ class Blog(FullyValidatedModel):
176
+ posts: int = 0
177
+ uuid: str = ""
178
+
179
+
173
180
  class ResponseModel(FullyValidatedModel):
174
181
  class Response(FullyValidatedModel):
175
- class Blog(FullyValidatedModel):
176
- posts: int
177
-
178
- blog: Blog = Blog(posts=0)
182
+ blog: Blog = Blog()
179
183
  posts: list[Any] = []
180
184
 
181
185
  response: Response
@@ -183,12 +187,18 @@ class ResponseModel(FullyValidatedModel):
183
187
 
184
188
  class Post(FullyValidatedModel):
185
189
  class Block(FullyValidatedModel):
186
- type: str
190
+ type: str = ""
187
191
  text: str = ""
188
192
  blocks: list[int] = []
189
193
 
194
+ blog: SkipJsonSchema[Blog] = Blog()
195
+ id: SkipJsonSchema[int] = 0
196
+ parent_tumblelog_uuid: SkipJsonSchema[str] = ""
197
+ parent_post_id: SkipJsonSchema[int] = 0
198
+ reblog_key: SkipJsonSchema[str] = ""
199
+
190
200
  timestamp: SkipJsonSchema[int] = 0
191
- tags: Annotated[list[str], PlainSerializer(",".join)]
201
+ tags: Annotated[list[str], PlainSerializer(",".join)] = []
192
202
  state: SkipJsonSchema[Literal["published", "queued", "draft", "private", "unapproved"]] = "published"
193
203
 
194
204
  content: SkipJsonSchema[list[Block]] = []
@@ -22,20 +22,25 @@ class TumblrSession(OAuth1Session):
22
22
  error.add_note(response.text)
23
23
  raise
24
24
 
25
- def retrieve_published_posts(self, blog_identifier: str, after: int) -> ResponseModel:
25
+ def retrieve_blog_info(self, blog_identifier: str) -> ResponseModel:
26
+ response = self.get(f"https://api.tumblr.com/v2/blog/{blog_identifier}/info")
27
+ return ResponseModel.model_validate_json(response.text)
28
+
29
+ def retrieve_published_posts(self, blog_identifier: str, offset: int | None = None, after: int | None = None) -> ResponseModel:
26
30
  response = self.get(
27
31
  f"https://api.tumblr.com/v2/blog/{blog_identifier}/posts",
28
32
  params={
33
+ "offset": offset,
29
34
  "after": after,
30
35
  "sort": "asc",
31
36
  "npf": True,
32
37
  },
33
38
  )
34
- return ResponseModel.model_validate_json(response.content)
39
+ return ResponseModel.model_validate_json(response.text)
35
40
 
36
41
  def create_post(self, blog_identifier: str, post: Post) -> ResponseModel:
37
42
  response = self.post(
38
43
  f"https://api.tumblr.com/v2/blog/{blog_identifier}/posts",
39
44
  json=post.model_dump(),
40
45
  )
41
- return ResponseModel.model_validate_json(response.content)
46
+ return ResponseModel.model_validate_json(response.text)
File without changes
File without changes
File without changes