Skip to content

Commit 3bb381a

Browse files
umputunclaude
andcommitted
Fix linter warnings
- Change octal literals to new 0o600 format - Remove unused parameters with blank identifiers - Combine parameter types in function signatures - Add security annotations for HTTP and subprocess calls - Pre-allocate slices with appropriate capacity 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4884938 commit 3bb381a

2 files changed

Lines changed: 109 additions & 15 deletions

File tree

.golangci.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
linters-settings:
2+
govet:
3+
shadow: true
4+
golint:
5+
min-confidence: 0.6
6+
gocyclo:
7+
min-complexity: 15
8+
maligned:
9+
suggest-new: true
10+
dupl:
11+
threshold: 100
12+
goconst:
13+
min-len: 2
14+
min-occurrences: 2
15+
misspell:
16+
locale: US
17+
lll:
18+
line-length: 140
19+
gocritic:
20+
enabled-tags:
21+
- performance
22+
- style
23+
- experimental
24+
disabled-checks:
25+
- wrapperFunc
26+
- hugeParam
27+
- rangeValCopy
28+
29+
linters:
30+
disable-all: true
31+
enable:
32+
- revive
33+
- govet
34+
- unconvert
35+
- gosec
36+
- misspell
37+
- unused
38+
- typecheck
39+
- ineffassign
40+
- stylecheck
41+
- gochecknoinits
42+
- gocritic
43+
- nakedret
44+
- gosimple
45+
- prealloc
46+
47+
fast: false
48+
49+
50+
run:
51+
# modules-download-mode: vendor
52+
53+
concurrency: 4
54+
55+
issues:
56+
exclude-dirs:
57+
- vendor
58+
exclude-rules:
59+
- text: "should have a package comment, unless it's in another file for this package"
60+
linters:
61+
- golint
62+
- text: "exitAfterDefer:"
63+
linters:
64+
- gocritic
65+
- text: "whyNoLint: include an explanation for nolint directive"
66+
linters:
67+
- gocritic
68+
- text: "go.mongodb.org/mongo-driver/bson/primitive.E"
69+
linters:
70+
- govet
71+
- text: "weak cryptographic primitive"
72+
linters:
73+
- gosec
74+
- text: "at least one file in a package should have a package comment"
75+
linters:
76+
- stylecheck
77+
- text: "package-comments: should have a package comment"
78+
linters:
79+
- revive
80+
- text: 'Deferring unsafe method "Close" on type "io.ReadCloser"'
81+
linters:
82+
- gosec
83+
- linters:
84+
- unparam
85+
- unused
86+
- revive
87+
path: _test\.go$
88+
text: "unused-parameter"
89+
exclude-use-default: false

main.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ func run(config Config) error {
165165
}
166166

167167
// fetchArticle downloads and extracts text from the given URL
168-
func fetchArticle(url string) (string, string, error) {
168+
func fetchArticle(url string) (content, title string, err error) {
169169
// Fetch the page
170+
// #nosec G107 -- URL is provided by command-line flag
170171
resp, err := http.Get(url)
171172
if err != nil {
172173
return "", "", err
@@ -184,21 +185,21 @@ func fetchArticle(url string) (string, string, error) {
184185
}
185186

186187
// Extract title
187-
title := doc.Find("title").Text()
188+
title = doc.Find("title").Text()
188189

189190
// Extract article content - this is a simplified approach
190191
var articleText strings.Builder
191192

192193
// First try to find article content in common containers
193194
article := doc.Find("article, .article, .post, .content, main")
194195
if article.Length() > 0 {
195-
article.Find("p").Each(func(i int, s *goquery.Selection) {
196+
article.Find("p").Each(func(_ int, s *goquery.Selection) {
196197
articleText.WriteString(s.Text())
197198
articleText.WriteString("\n\n")
198199
})
199200
} else {
200201
// Fallback to all paragraphs
201-
doc.Find("p").Each(func(i int, s *goquery.Selection) {
202+
doc.Find("p").Each(func(_ int, s *goquery.Selection) {
202203
// Skip very short paragraphs which are likely not article content
203204
if len(s.Text()) > 50 {
204205
articleText.WriteString(s.Text())
@@ -208,7 +209,7 @@ func fetchArticle(url string) (string, string, error) {
208209
}
209210

210211
// Limit article length for API calls
211-
content := articleText.String()
212+
content = articleText.String()
212213
if len(content) > 8000 {
213214
content = content[:8000] + "..."
214215
}
@@ -232,7 +233,7 @@ type OpenAIRequest struct {
232233

233234
// prepareHostDescriptions formats host information for the prompt
234235
func prepareHostDescriptions(hosts []Host) string {
235-
var hostDescriptions []string
236+
hostDescriptions := make([]string, 0, len(hosts))
236237
for _, host := range hosts {
237238
hostDescriptions = append(hostDescriptions,
238239
fmt.Sprintf("%s (%s): %s", host.Name, host.Gender, host.Character))
@@ -241,7 +242,7 @@ func prepareHostDescriptions(hosts []Host) string {
241242
}
242243

243244
// createDiscussionPrompt creates the system prompt for the discussion
244-
func createDiscussionPrompt(hostDescriptions string, targetMessages int, targetDuration int) string {
245+
func createDiscussionPrompt(hostDescriptions string, targetMessages, targetDuration int) string {
245246
return fmt.Sprintf(`Generate a heated and passionate tech podcast discussion in Russian language between these hosts about the following article:
246247
247248
%s
@@ -477,14 +478,14 @@ func generateSpeechSegments(messages []Message, hostMap map[string]struct {
477478
msg.Host, i+1, len(messages))
478479

479480
// Generate speech with OpenAI TTS
480-
audioData, err := generateOpenAITTS(msg.Content, "ru", gender, voice, speechSpeed, apiKey)
481+
audioData, err := generateOpenAITTS(msg.Content, gender, voice, speechSpeed, apiKey)
481482
if err != nil {
482483
return nil, fmt.Errorf("failed to generate speech for message %d: %w", i, err)
483484
}
484485

485486
// Create a file for the audio
486487
filename := fmt.Sprintf("%s/segment_%03d.mp3", tempDir, i)
487-
err = os.WriteFile(filename, audioData, 0644)
488+
err = os.WriteFile(filename, audioData, 0o600)
488489
if err != nil {
489490
return nil, fmt.Errorf("failed to write audio data: %w", err)
490491
}
@@ -514,6 +515,7 @@ func streamToIcecast(concatFile string, config Config) error {
514515
icecastURL,
515516
}
516517

518+
// #nosec G204 -- Arguments are constructed internally, not from external input
517519
cmd := exec.Command("ffmpeg", args...)
518520
cmd.Stdout = os.Stdout
519521
cmd.Stderr = os.Stderr
@@ -611,7 +613,7 @@ func speechGenerationWorker(requestChan <-chan SpeechGenerationRequest, resultCh
611613
case req := <-requestChan:
612614
segmentStartTime := time.Now()
613615
fmt.Printf("Generating speech for message %d from %s...\n", req.index, req.msg.Host)
614-
audioData, err := generateOpenAITTS(req.msg.Content, "ru", req.gender, req.voice, req.speed, req.apiKey)
616+
audioData, err := generateOpenAITTS(req.msg.Content, req.gender, req.voice, req.speed, req.apiKey)
615617
if err != nil {
616618
fmt.Printf("Error generating speech for message %d: %v\n", req.index, err)
617619
} else {
@@ -658,15 +660,15 @@ func saveToConcatFile(tempDir string, audioFiles []string) (string, error) {
658660
for _, file := range audioFiles {
659661
concatContent.WriteString(fmt.Sprintf("file '%s'\n", file))
660662
}
661-
err := os.WriteFile(concatFile, []byte(concatContent.String()), 0644)
663+
err := os.WriteFile(concatFile, []byte(concatContent.String()), 0o600)
662664
if err != nil {
663665
return "", fmt.Errorf("failed to write concat file: %w", err)
664666
}
665667
return concatFile, nil
666668
}
667669

668670
// concatenateAudioFiles uses ffmpeg to concatenate audio files into a single output file
669-
func concatenateAudioFiles(concatFile string, outputFile string) error {
671+
func concatenateAudioFiles(concatFile, outputFile string) error {
670672
fmt.Println("Concatenating audio files...")
671673
concatStartTime := time.Now()
672674

@@ -680,6 +682,7 @@ func concatenateAudioFiles(concatFile string, outputFile string) error {
680682
outputFile,
681683
}
682684

685+
// #nosec G204 -- Arguments are constructed internally, not from external input
683686
cmd := exec.Command("ffmpeg", args...)
684687
cmd.Stdout = os.Stdout
685688
cmd.Stderr = os.Stderr
@@ -784,7 +787,7 @@ func generateAndPlayLocally(discussion Discussion, config Config) error {
784787
// Create a temporary file for the audio
785788
filename := fmt.Sprintf("%s/segment_%03d.mp3", tempDir, playedIndex)
786789
fmt.Printf("Writing segment %d to file %s...\n", playedIndex, filename)
787-
err := os.WriteFile(filename, nextSegment.AudioData, 0644)
790+
err := os.WriteFile(filename, nextSegment.AudioData, 0o600)
788791
if err != nil {
789792
fmt.Printf("Error writing segment %d: %v\n", playedIndex, err)
790793
close(stopChan)
@@ -863,8 +866,10 @@ func playAudioFile(filename string) error {
863866
for _, player := range players {
864867
if _, err := exec.LookPath(player); err == nil {
865868
if player == "aplay" {
869+
// #nosec G204 -- Player is selected from a whitelist of known audio players
866870
cmd = exec.Command(player, "-q", filename)
867871
} else {
872+
// #nosec G204 -- Player is selected from a whitelist of known audio players
868873
cmd = exec.Command(player, filename, "-nodisp", "-autoexit", "-really-quiet")
869874
}
870875
break
@@ -1016,7 +1021,7 @@ func callOpenAITTSAPI(request OpenAITTSRequest, apiKey string) ([]byte, error) {
10161021
}
10171022

10181023
// generateOpenAITTS uses OpenAI's Chat Completions API to generate natural speech
1019-
func generateOpenAITTS(text, lang, gender, voice string, speed float64, apiKey string) ([]byte, error) {
1024+
func generateOpenAITTS(text, _ /* gender */, voice string, _ /* speed */ float64, apiKey string) ([]byte, error) {
10201025
// Get the appropriate speaking style for this voice
10211026
speakingStyle := getSpeakingStyle(voice)
10221027

@@ -1058,4 +1063,4 @@ func truncateString(s string, maxLength int) string {
10581063
}
10591064

10601065
return s[:maxLength] + "..."
1061-
}
1066+
}

0 commit comments

Comments
 (0)