Skip to content

Commit b5ce462

Browse files
committed
Podcast: simplify newsletter references
Removes the need for boilerplate logic when adding newsletter references The new logic requires just the podcast timestamps to be added in the related newsletter sections. For each podcast, the `recap_references_generator` will parse the `reference` newsletter (declared in frontmatter) looking for podcast reference marks (timestamps) in order to create the newsletter references for the podcast page.
1 parent 675853d commit b5ce462

23 files changed

+305
-739
lines changed

_includes/functions/podcast-callout.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

_includes/functions/podcast-note.md

Lines changed: 0 additions & 8 deletions
This file was deleted.

_includes/newsletter-references.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{% assign newsletter_sections = page.references | group_by:"header" %}
2+
{% assign header_names = newsletter_sections | map: "name" %}
3+
{% unless header_names contains "News" %}
4+
## News
5+
*No significant news this week was found on the Bitcoin-Dev or Lightning-Dev mailing lists.*
6+
{% endunless %}
7+
8+
{% for section in newsletter_sections %}
9+
## {{ section.name }}
10+
<ul>
11+
{% for reference in section.items %}
12+
{% assign podcast_slug = reference.podcast_slug | default: reference.slug %}
13+
<li id="{{ podcast_slug | slice: 1, podcast_slug.size }}" class="anchor-list">
14+
<p>
15+
<a href="{{ podcast_slug }}" class="anchor-list-link">●</a>
16+
{{ reference.title }}
17+
(<a title="Play this segment of the podcast" onClick="seek('{{reference.timestamp}}')" class="seek">{{reference.timestamp}}</a><noscript>{{reference.timestamp}}</noscript>)
18+
<a href="{{page.reference}}{{reference.slug}}">
19+
<i class="fa fa-link" title="Link to related content"></i>
20+
</a>
21+
<!--<a onClick="seek('{{include.timestamp}}')" class="seek"><i class="fa
22+
fa-headphones" title="Play this segment of the podcast"></i></a>-->
23+
{% if reference.has_transcript_section %}
24+
<a href="{{reference.slug}}-transcript">
25+
<i class="fa fa-file-text-o" title="Read this segment of the transcription"></i>
26+
</a>
27+
{% endif %}
28+
</p>
29+
</li>
30+
{% endfor %}
31+
</ul>
32+
{% endfor %}

_plugins/auto-anchor.rb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ def auto_anchor(content)
1616
## No match, pass item through unchanged
1717
string
1818
else
19-
## Remove double-quotes from titles before attempting to slugify
20-
title.gsub!('"', '')
21-
## Use Liquid/Jekyll slugify filter to choose our id
22-
slug = "\#{{ \"#{title}\" | slugify: 'latin' }}"
19+
slug = generate_slug(title)
2320
id_prefix = "- {:#{slug} .anchor-list} <a href=\"#{slug}\" class=\"anchor-list-link\">●</a>"
2421
string.sub!(/-/, id_prefix)
2522
end

_plugins/common_utils.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
def generate_slug(title)
2+
## Remove double-quotes from titles before attempting to slugify
3+
title.gsub('"', '')
4+
## Use Liquid/Jekyll slugify filter to choose our id
5+
liquid_string = "\#{{ \"#{title}\" | slugify: 'latin' }}"
6+
slug = Liquid::Template.parse(liquid_string)
7+
# An empty context is used here because we only need to parse the liquid
8+
# string and don't require any additional variables or data.
9+
slug.render(Liquid::Context.new)
10+
end
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# frozen_string_literal: true
2+
3+
# Regex pattern to match "{% assign timestamp="xx:xx:xx" %}"
4+
$podcast_reference_mark = /\{%\s*assign\s+timestamp\s*=\s*"([^"]+)"\s*%\}/
5+
6+
# Create the podcast recap references by parsing the referenced newsletter for
7+
# podcast reference marks (timestamps)
8+
class RecapReferencesGenerator < Jekyll::Generator
9+
def generate(site)
10+
podcast_pages = site.documents.select { |doc| doc.data["type"] == "podcast"}
11+
podcast_pages.each do |podcast|
12+
# podcast episodes have a "reference" field that indicates the related newsletter page
13+
unless podcast.data["reference"].nil?
14+
reference_page = site.documents.detect { |page| page.url == podcast.data["reference"] }
15+
16+
# override the content of the reference page (newsletter) to now include
17+
# the links to the related podcast items
18+
reference_page.content,
19+
# keep all the references in a podcast page variable to use them later
20+
# during the podcast page creation
21+
podcast.data["references"] = get_podcast_references(reference_page.content, podcast.url)
22+
23+
# Each podcast transcript splits into segements using the paragraph title
24+
# as the title of the segment. These segment splits are added manually but
25+
# we can avoid the need to also manually add their anchors by doing that here
26+
podcast.data["references"].each do |reference|
27+
reference["has_transcript_section"] = podcast.content.sub!(/^(_.*?#{Regexp.escape(reference["title"])}.*?_)/, "{:#{reference["slug"]}-transcript}\n \\1")
28+
end
29+
end
30+
end
31+
end
32+
33+
34+
def find_title(string, in_list=true, slugify=true)
35+
# this conditional prefix is for the special case of the review club section
36+
# which is not a list item (no dash (-) at the start of the line)
37+
prefix = in_list ? / *- / : //
38+
39+
# Find shortest match for **bold**, or [markdown][links]
40+
# note: when we are matching the title in `auto-anchor.rb` we also match *italics*
41+
# but on the newsletter sections nested bullets have *italics* titles therefore
42+
# by ignoring *italics* we are able to easier link to the outer title
43+
title = string.match(/^#{prefix}(?:\*\*(.*?):?\*\*|\[(.*?):?\][(\[])/)&.captures&.compact&.[](0) || ""
44+
if title.empty?
45+
{}
46+
else
47+
result = {"title"=> title}
48+
slug = slugify ? {"slug"=> generate_slug(title)} : {}
49+
result.merge!(slug)
50+
end
51+
end
52+
53+
# This method searches the content for paragraphs that indicate that they are
54+
# part of a podcast recap. When a paragraph is part of a recap we:
55+
# - postfix with a link to the related podcast item
56+
# - get the header, title and title slug of the paragraph to create
57+
# the references for the podcast
58+
def get_podcast_references(content, target_page_url)
59+
# The logic here assumes that:
60+
# - paragraphs have headers
61+
# - each block of text (paragraph) is seperated by an empty line
62+
63+
# Split the content into paragraphs
64+
paragraphs = content.split(/\n\n+/)
65+
# Find all the headers in the content
66+
headers = content.scan(/^#+\s+(.*)$/).flatten
67+
68+
# Create an array of hashes containing:
69+
# - the paragraph's title
70+
# - the paragraph's title slug
71+
# - the associated header
72+
# - the timestamp of the podcast in which this paragraph is discussed
73+
podcast_references = []
74+
current_header = 0
75+
current_title = {}
76+
in_review_club_section = false
77+
78+
# Iterate over all paragraphs to find those with a podcast reference mark
79+
paragraphs.each do |p|
80+
# a title might have multiple paragraphs associated with it
81+
# the podcast reference mark might be at the end of an isolated
82+
# paragraph snippet that cannot access the title, therefore
83+
# we keep this information to be used in the link to the podcast recap
84+
title = find_title(p, !in_review_club_section)
85+
if !title.empty?
86+
# paragraph has title
87+
current_title = title
88+
end
89+
90+
# If the current paragraph contains the podcast reference mark,
91+
# capture the timestamp, add paragraph to references and replace
92+
# the mark with link to the related podcast item
93+
p.gsub!($podcast_reference_mark) do |match|
94+
if in_review_club_section
95+
# the newsletter's review club section is the only section that does
96+
# not have a list item to use as anchor so we use the header
97+
current_title["podcast_slug"] = "#pr-review-club" # to avoid duplicate anchor
98+
current_title["slug"] = "#bitcoin-core-pr-review-club"
99+
end
100+
podcast_reference = {"header"=> headers[current_header], "timestamp"=> $1}
101+
podcast_reference.merge!(current_title)
102+
podcast_references << podcast_reference
103+
104+
# Replace the whole match with the link
105+
headphones_link = "[<i class='fa fa-headphones' title='Listen to our discussion of this on the podcast'></i>]"
106+
replacement_link_to_podcast_item = "#{headphones_link}(#{target_page_url}#{current_title["podcast_slug"] || current_title["slug"]})"
107+
end
108+
109+
# update to the next header when parse through it
110+
if p.sub(/^#+\s*/, "") == headers[(current_header + 1) % headers.length()]
111+
current_header += 1
112+
in_review_club_section = headers[current_header] == "Bitcoin Core PR Review Club"
113+
end
114+
115+
end
116+
117+
# Join the paragraphs back together to return the modified content
118+
updated_content = paragraphs.join("\n\n")
119+
120+
[updated_content, podcast_references]
121+
end
122+
end

_posts/en/newsletters/2023-02-15-newsletter.md

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ technical documentation and discussion.
6969
more than 83 bytes of arbitrary data. They reasoned that other
7070
methods for storing large amounts of data are currently in use and
7171
there would be no additional harm from `OP_RETURN` being used
72-
instead. {% include functions/podcast-callout.md url="pod238 storage" %}
72+
instead. {% assign timestamp="1:02" %}
7373

7474
- **Fee dilution in multiparty protocols:** Yuval Kogman
7575
[posted][kogman dilution] to the Bitcoin-Dev mailing list the
@@ -104,7 +104,7 @@ technical documentation and discussion.
104104
Kogman describes several mitigations in his post, although all of them
105105
involve tradeoffs. In a [second post][kogman dilution2], he notes
106106
that he's unaware of any currently deployed protocol that's
107-
vulnerable. {% include functions/podcast-callout.md url="pod238 dilution" %}
107+
vulnerable. {% assign timestamp="27:29" %}
108108

109109
- **Tapscript signature malleability:** in an aside to the above-mentioned
110110
conversation about fee dilution, developer Russell O'Connor
@@ -133,8 +133,7 @@ technical documentation and discussion.
133133
opened an [issue][bitcoin inquisition #19] to the Bitcoin
134134
Inquisition repository being used to test [SIGHASH_ANYPREVOUT][topic
135135
sighash_anyprevout] (APO) to consider having APO commit to additional
136-
data to prevent this issue for users of that extension. {% include
137-
functions/podcast-callout.md url="pod238 tapscript" %}
136+
data to prevent this issue for users of that extension. {% assign timestamp="33:47" %}
138137
139138
## Changes to services and client software
140139
@@ -143,34 +142,30 @@ wallets and services.*
143142
144143
- **Liana wallet adds multisig:**
145144
[Liana][news234 liana]'s [0.2 release][liana 0.2] adds multisig support using
146-
[descriptors][topic descriptors]. {% include functions/podcast-callout.md
147-
url="pod238 sc1" %}
145+
[descriptors][topic descriptors]. {% assign timestamp="37:35" %}
148146
149147
- **Sparrow wallet 1.7.2 released:**
150148
Sparrow's [1.7.2 release][sparrow 1.7.2] adds [taproot][topic taproot]
151149
support, [BIP329][] import and export features (see [Newsletter #235][news235
152-
bip329]), and additional support for hardware signing devices. {% include
153-
functions/podcast-callout.md url="pod238 sc2" %}
150+
bip329]), and additional support for hardware signing devices. {% assign timestamp="38:46" %}
154151
155152
- **Bitcoinex library adds schnorr support:**
156153
[Bitcoinex][bitcoinex github] is a Bitcoin utility library for the Elixir
157-
functional programming language. {% include functions/podcast-callout.md
158-
url="pod238 sc3" %}
154+
functional programming language. {% assign timestamp="39:20" %}
159155
160156
- **Libwally 0.8.8 released:**
161157
[Libwally 0.8.8][] adds [BIP340][] tagged hash support, additional sighash
162158
support including [BIP118][] ([SIGHASH_ANYPREVOUT][topic SIGHASH_ANYPREVOUT]), and
163159
additional [miniscript][topic miniscript], descriptor, and [PSBT][topic psbt]
164-
functions. {% include functions/podcast-callout.md url="pod238 sc4" %}
160+
functions. {% assign timestamp="39:59" %}
165161
166162
## Optech Recommends
167163
168-
[BitcoinSearch.xyz][] is a recently-launched search engine for Bitcoin
164+
- [BitcoinSearch.xyz][] is a recently-launched search engine for Bitcoin
169165
technical documentation and discussions. It was used to quickly find
170166
several of the sources linked in this newsletter, a vast improvement
171167
over other more laborious methods we've previously used. Contributions
172-
to its [code][bitcoinsearch repos] are welcome. {% include
173-
functions/podcast-callout.md url="pod238 bitcoinsearch" %}
168+
to its [code][bitcoinsearch repos] are welcome. {% assign timestamp="40:40" %}
174169
175170
## Releases and release candidates
176171
@@ -179,20 +174,17 @@ projects. Please consider upgrading to new releases or helping to test
179174
release candidates.*
180175
181176
- [Core Lightning 23.02rc2][] is a release candidate for a new
182-
maintenance version of this popular LN implementation. {% include
183-
functions/podcast-callout.md url="pod238 cln" %}
177+
maintenance version of this popular LN implementation. {% assign timestamp="42:21" %}
184178
185179
- [BTCPay Server 1.7.11][] is a new release. Since the last release we
186180
covered (1.7.1), several new features have been added and many bug
187181
fixes and improvements have been made. Especially notable, several
188182
aspects related to plugins and third-party integrations have been
189183
changed, a migration path away from legacy MySQL and SQLite has been
190-
added, and a cross-site scripting vulnerability has been fixed. {% include
191-
functions/podcast-callout.md url="pod238 btcpay" %}
184+
added, and a cross-site scripting vulnerability has been fixed. {% assign timestamp="47:49" %}
192185
193186
- [BDK 0.27.0][] is an update to this library for building Bitcoin
194-
wallets and applications. {% include
195-
functions/podcast-callout.md url="pod238 bdk" %}
187+
wallets and applications. {% assign timestamp="49:32" %}
196188
197189
## Notable code and documentation changes
198190
@@ -214,15 +206,15 @@ Proposals (BIPs)][bips repo], and [Lightning BOLTs][bolts repo].*
214206
backups]. The merged PR adds support for creating, storing, and retrieving
215207
the encrypted backups. As noted in the commit messages, the feature
216208
hasn't yet been fully specified or adopted by other LN
217-
implementations. {% include functions/podcast-callout.md url="pod238 cln5361" %}
209+
implementations. {% assign timestamp="49:51" %}
218210
219211
- [Core Lightning #5670][] and [#5956][core lightning #5956] make
220212
various updates to its implementation of [dual funding][topic dual
221213
funding] based on both recent changes to the [specification][bolts
222214
#851] and comments from interoperability testers. Additionally, an
223215
`upgradewallet` RPC is added to move all funds in P2SH-wrapped outputs
224216
to native segwit outputs, which is required for interactive channel
225-
opens. {% include functions/podcast-callout.md url="pod238 cln5670" %}
217+
opens. {% assign timestamp="54:45" %}
226218
227219
- [Core Lightning #5697][] adds a `signinvoice` RPC that will sign a
228220
[BOLT11][] invoice. Previously, CLN would only sign an invoice when
@@ -235,20 +227,20 @@ Proposals (BIPs)][bips repo], and [Lightning BOLTs][bolts repo].*
235227
your node can claim that payment before it arrives. That not only
236228
steals your money but, because you signed the invoice, generates very
237229
convincing evidence that you were paid (this evidence is so convincing
238-
that many LN developers call it *proof of payment*). {% include functions/podcast-callout.md url="pod238 cln5697" %}
230+
that many LN developers call it *proof of payment*). {% assign timestamp="56:23" %}
239231
240232
- [Core Lightning #5960][] adds a [security policy][cln security.md]
241-
that includes contact addresses and PGP keys. {% include functions/podcast-callout.md url="pod238 cln5960" %}
233+
that includes contact addresses and PGP keys. {% assign timestamp="57:38" %}
242234
243235
- [LND #7171][] upgrades the `signrpc` RPC <!--sic--> to support the
244236
latest [draft BIP][musig draft bip] for [MuSig2][topic musig]. The RPC now creates
245237
sessions linked to a MuSig2 protocol version number so that all
246238
operations within a session will use the correct protocol. A
247239
security issue with an older version of the MuSig2 protocol was
248-
mentioned in [Newsletter #222][news222 musig2]. {% include functions/podcast-callout.md url="pod238 lnd7171" %}
240+
mentioned in [Newsletter #222][news222 musig2]. {% assign timestamp="58:54" %}
249241
250242
- [LDK #2002][] adds support for automatically resending [spontaneous
251-
payments][topic spontaneous payments] that don't initially succeed. {% include functions/podcast-callout.md url="pod238 ldk2002" %}
243+
payments][topic spontaneous payments] that don't initially succeed. {% assign timestamp="1:00:01" %}
252244
253245
- [BTCPay Server #4600][] updates the [coin selection][topic coin selection] for its [payjoin][topic payjoin]
254246
implementation to try to avoid creating transactions with *unnecessary
@@ -257,7 +249,7 @@ Proposals (BIPs)][bips repo], and [Lightning BOLTs][bolts repo].*
257249
regular single-spender, single-receiver payment: the largest input
258250
would have provided sufficient payment for the payment output and no
259251
additional inputs would have been added.
260-
This PR was partly inspired by a [paper analyzing payjoins][]. {% include functions/podcast-callout.md url="pod238 btcpay4600" %}
252+
This PR was partly inspired by a [paper analyzing payjoins][]. {% assign timestamp="1:00:59" %}
261253
262254
{% include references.md %}
263255
{% include linkers/issues.md v=2 issues="5361,5670,5956,851,5697,5960,7171,2002,4541,4600" %}
@@ -288,21 +280,3 @@ Proposals (BIPs)][bips repo], and [Lightning BOLTs][bolts repo].*
288280
[news235 bip329]: /en/newsletters/2023/01/25/#bips-1383
289281
[bitcoinex github]: https://github.com/RiverFinancial/bitcoinex
290282
[libwally 0.8.8]: https://github.com/ElementsProject/libwally-core/releases/tag/release_0.8.8
291-
[pod238 storage]: /en/podcast/2023/02/16/#continued-discussion-about-block-chain-data-storage
292-
[pod238 dilution]: /en/podcast/2023/02/16/#fee-dilution-in-multiparty-protocols
293-
[pod238 tapscript]: /en/podcast/2023/02/16/#tapscript-signature-malleability
294-
[pod238 sc1]: /en/podcast/2023/02/16/#liana-wallet-adds-multisig
295-
[pod238 sc2]: /en/podcast/2023/02/16/#sparrow-wallet-1-7-2-released
296-
[pod238 sc3]: /en/podcast/2023/02/16/#bitcoinex-library-adds-schnorr-support
297-
[pod238 sc4]: /en/podcast/2023/02/16/#libwally-0-8-8-released
298-
[pod238 bitcoinsearch]: /en/podcast/2023/02/16/#bitcoinsearch-xyz
299-
[pod238 cln]: /en/podcast/2023/02/16/#core-lightning-23-02rc2
300-
[pod238 btcpay]: /en/podcast/2023/02/16/#btcpay-server-1-7-11
301-
[pod238 bdk]: /en/podcast/2023/02/16/#bdk-0-27-0
302-
[pod238 cln5361]: /en/podcast/2023/02/16/#core-lightning-5361
303-
[pod238 cln5670]: /en/podcast/2023/02/16/#core-lightning-5670-and-5956
304-
[pod238 cln5697]: /en/podcast/2023/02/16/#core-lightning-5697
305-
[pod238 cln5960]: /en/podcast/2023/02/16/#core-lightning-5960
306-
[pod238 lnd7171]: /en/podcast/2023/02/16/#lnd-7171
307-
[pod238 ldk2002]: /en/podcast/2023/02/16/#ldk-2002
308-
[pod238 btcpay4600]: /en/podcast/2023/02/16/#btcpay-server-4600

0 commit comments

Comments
 (0)